2017年2月18日 星期六

機器學習(5)--邏輯斯迴歸,過度適合與正規化( Logistic regression,overfitting and regularization)


  本節介紹邏輯斯迴歸模型(Logistic regression)過度適合(overfitting)正規化(regularization)。邏輯斯迴歸模型(Logistic regression)能處裡線性和二元分類的問題,要注意的是這裡所要說的邏輯斯迴歸模型(Logistic regression)是分類模型而無關於"迴歸"。

邏輯斯迴歸模型(Logistic regression):

首先來看一下邏輯斯迴歸模型(Logistic regression),如下圖跟前一章所介紹的
適應線性神經元模型類似,但是其所使用的啟動函數不同,成本函數也不同。

<圖一>邏輯斯迴歸模型(Logistic regression)




邏輯斯迴歸模型(Logistic regression)使用啟動函數稱之為sigmoid函數:

z為淨輸入:


其sigmoid函數圖型看似為S形狀。淨輸入z當作sigmoid函數的輸入後,會被轉成0.0~1.0的實數分布,如果截距給定0.5,那我們可以利用二元量化器,如果Φ(z)>=0.5 便分為正類,反之分為負類。sigmoid函數的輸出可以解釋為"某特定樣本,給定特徵x與加權w,當參數時屬於類別1的機率":

例如以之前的鳶尾花樣本為分類範例,如果計算出某特定樣本Φ(z)=0.8,而分類1假設為樣本Iris
-Versicolor,那麼意味著該特定樣本有80%的機率是屬於Iris-Versicolor分類:而屬於另一樣本(假設為Iris-Setosa)的機率為20%。可以底下公式算出:


接著,便可以使用一量化器,將預測機率轉換成二元分類的結果:



其等價於如下,z為淨輸入:
原因是當輸入z為趨近於正無限大,那麼Φ(z)輸出會趨近於1.0,輸入z為趨近於負無限大,那麼Φ(z)輸出會趨近於0.0。

<圖二>sigmoid函數


邏輯斯迴歸模型(Logistic regression)的成本函數:

    為了解釋如何推導邏輯斯迴歸模型的成本函數,我們先定義"概似"L(likelihood),我們期望L最大,也就是我們期望"某特定樣本屬於某一特定分類的機率最大"。假設我們數據集中的各個樣本是互相獨立的,那麼計算L的公式如下:
要對上述公式求取最大化,可以對這個公式套用自然對數函數,這就是所謂的"對數概似函數"(log-likelihood function):


現在可以使用最佳化演算法,如梯度上升法來最大化這個概似函數,在此我們也可以把這個概似函數寫為成本函數,也就是取負,讓函數開口變上,那麼就可以反過來使用之前所說的梯度下降法(GD)來最小化這個成本函數J(w):



為了更理解這個成本函數,讓我們來看看單一實例的成本如何計算:
 觀上述方程式如果y=0,那麼第一項為0,如果y=1,那麼第二項為0,如下所示:
我們以下圖示來表示:
    可以看到如果正確的預測一個樣本屬於1這類,那麼成本會接近0(實心線),同樣的,如果
正確的預測樣本y=0這類(虛線),其成本也是接近0。反之,如果分類錯誤,成本會變無窮大。
我們視一個極大的成本,當做是分類錯誤。

<圖三>單一樣本實例,對不同Φ(z)值的分類"成本函數"J




以正規化(regularization)處裡高度適合(overfitting)現象:

    高度適合(overfitting )是機器學習常見的一種現象,意旨一個模型在對"訓練數據集"時有很好的效能,但是面對"未知的數據集"或是"測試數據集"時,卻效能不佳。如果該模型有高度適合(overfitting )現象,也代表著有高變異性(high variance)其產生的原因可能是使用"過多的特徵",而相反的低度適合(underfitting)則代表有著高偏誤(high bias),其模型在訓練樣本時無法訓練出適合的模式,而在面對"未知的數據"時,通常也不會有好的效能。

    變異數(variance)可以測量該模型對特定樣本,預測能力的一致性或是變異性,也就是說該模型對訓練數據的隨機性,是否反應良好。
    偏誤(bias)一般而言是測量預測正確值的偏離,在於描述不是由隨機性所產生的系統誤差

    可以用下圖來理解這些狀況:


     若想要在"偏誤(bias)-變異數(variance)"之間找到一個平衡,其中一個方法是透過正規化(regularization),來調整模型的複雜度。正規化(regularization)防止高度適合(overfitting )的觀念是
加入更多的偏誤(bias)來懲罰極端的參數權重,最常見的是"L2正規化",也被稱為L2收縮或加權衰減,其表示式為:


λ稱之為正規化參數(regularization parameter)
    要運用正規化,只需要將正規項加入成本函數,用它來縮小邏輯斯迴歸的加權,如下:

藉由正規化參數(regularization parameter)λ,我們可以讓加權保持很小。另一方面如果加大正規化參數(regularization parameter)λ,則可以增加正規化的強度。

    在Scikit-learn 裡LogisticRegression類別的參數C,命名方式來自於SVM支援向量機,C定義為正規化參數(regularization parameter)λ的倒數:

我們可以重寫邏輯斯迴歸的正規化成本函數:

    因此,降低反正規化參數(regularization parameter)C,也就是意謂著增加正規化參數(regularization parameter)λ的值,進而增加正規化強度,底下我們可以藉由繪製L2正規化兩個加權系數的變動路徑看出:
     底下結果顯示將C變小,權重係數就會縮收,因此增加正規化強度。

<圖四_範例程式1>反正規化參數(regularization parameter)C 對權重係數調整的影響




<範例程式1:>

from sklearn import datasets
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import warnings,math
from sklearn.linear_model import LogisticRegression

#load iris data
iris = datasets.load_iris()
X = iris.data[:, [2, 3]]
y = iris.target
#分成30%測試資料,70% 訓練資料
X_train, X_test, y_train, y_test = train_test_split(
 X, y, test_size=0.3, random_state=0)
#標準化資料
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

print("X_train_std.shape",X_train_std.shape)
print("X_test_std.shape",X_test_std.shape)

weights, params = [], []
for c in np.arange(-5, 5):
 CC=math.pow(10,c)
 lr = LogisticRegression(C=CC, random_state=0)
 lr.fit(X_train_std, y_train)
 weights.append(lr.coef_[1])
 params.append(CC)

weights = np.array(weights)
plt.plot(params, weights[:, 0],
   label='petal length')
plt.plot(params, weights[:, 1], linestyle='--',
   label='petal width')
plt.ylabel('weight coefficient')
plt.xlabel('C')
plt.legend(loc='upper left')
plt.xscale('log')
plt.show()



<圖五>以邏輯斯迴歸模型(Logistic regression)分類鳶尾花數據



<邏輯斯迴歸模型(Logistic regression):>

from numpy.random import seed
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import ListedColormap
from sklearn.datasets import load_iris


def plot_decision_regions(X, y, classifier, resolution=0.02):
 # setup marker generator and color map
 markers = ('s', 'x', 'o', '^', 'v')
 colors = ('red', 'green', 'lightgreen', 'gray', 'cyan')
 cmap = ListedColormap(colors[:len(np.unique(y))])
 # plot the decision surface
 x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1 #feature 1
 x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1 #feature 2
 
 xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
         np.arange(x2_min, x2_max, resolution))
 Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
 Z = Z.reshape(xx1.shape)

 plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
 plt.xlim(xx1.min(), xx1.max())
 plt.ylim(xx2.min(), xx2.max())

 # plot class samples
 for idx, cl in enumerate(np.unique(y)):
  plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
     alpha=0.8, c=cmap(idx),
     marker=markers[idx], label=cl)

class LogisticRegressionGD(object):
    """Logistic regression classifier via gradient descent.

    Parameters
    ------------
    eta : float
        Learning rate (between 0.0 and 1.0)
    n_iter : int
        Passes over the training dataset.

    Attributes
    -----------
    w_ : 1d-array
        Weights after fitting.
    errors_ : list
        Number of misclassifications in every epoch.

    """
    def __init__(self, eta=0.01, n_iter=50):
        self.eta = eta
        self.n_iter = n_iter

    def fit(self, X, y):
        """ Fit training data.
        Parameters
        ----------
        X : {array-like}, shape = [n_samples, n_features]
            Training vectors, where n_samples is the number of samples and
            n_features is the number of features.
        y : array-like, shape = [n_samples]
            Target values.
        Returns
        -------
        self : object
        """
        self.w_ = np.zeros(1 + X.shape[1])
        self.cost_ = []
        
        for i in range(self.n_iter):
            net_input = self.net_input(X)
            output = self.activation(X)
            errors = (y - output)
            self.w_[1:] += self.eta * X.T.dot(errors)
            self.w_[0] += self.eta * errors.sum()
          
            # note that we compute the logistic `cost` now
            # instead of the sum of squared errors cost
            cost = -y.dot(np.log(output)) - ((1 - y).dot(np.log(1 - output)))
            self.cost_.append(cost)
        return self

    def net_input(self, X):
        """Calculate net input"""
        return np.dot(X, self.w_[1:]) + self.w_[0]

    def predict(self, X):
        """Return class label after unit step"""
        # We use the more common convention for logistic
        # regression returning class labels 0 and 1
        # instead of -1 and 1. Also, the threshold then
        # changes from 0.0 to 0.5 
        return np.where(self.activation(X) >= 0.5, 1, 0)
    
    # The Content of `activation` changed 
    # from linear (Adaline) to sigmoid.
    # Note that this method is now returning the
    # probability of the positive class
    # also "predict_proba" in scikit-learn
    def activation(self, X):
        """ Compute sigmoid activation."""
        z = self.net_input(X)
        sigmoid = 1.0 / (1.0 + np.exp(-z))
        return sigmoid
#直接使用scikit-learn載入Iris資料集
iris = load_iris()
#只取兩個特徵及兩類的數據
X, y = iris.data[:100, [0, 2]], iris.target[:100]
# standardize features
X_std = np.copy(X)
X_std[:, 0] = (X[:, 0] - X[:, 0].mean()) / X[:, 0].std()
X_std[:, 1] = (X[:, 1] - X[:, 1].mean()) / X[:, 1].std() 

lr = LogisticRegressionGD(n_iter=25, eta=0.15)
lr.fit(X_std, y)

plt.subplot(211)
plot_decision_regions(X_std, y, classifier=lr)
plt.title('Logistic Regression - Gradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('sepal width [standardized]')
plt.legend(loc='upper left')

plt.subplot(212)
plt.plot(range(1, len(lr.cost_) + 1), lr.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Logistic Cost')
plt.tight_layout()
plt.show()





<參考資料>書名:Python機器學習,作者:Sebastian Raschka

加入阿布拉機的3D列印與機器人的FB專頁
https://www.facebook.com/arbu00/


<其他相關文章>
人工神經網路(1)--使用Python實作perceptron(感知器)
人工神經網路(2)--使用Python實作後向傳遞神經網路演算法(Backprogation artificial neature network)
深度學習(1)-如何在windows安裝Theano +Keras +Tensorflow並使用GPU加速訓練神經網路
機器學習(1)--使用OPENCV KNN實作手寫辨識
機器學習(2)--使用OPENCV SVM實作手寫辨識
演算法(1)--蒙地卡羅法求圓周率及橢圓面積(Monte carlo)
機器學習(3)--適應線性神經元與梯度下降法(Adaline neuron and Gradient descent)
機器學習(4)--資料標準常態化與隨機梯度下降法( standardization & Stochastic Gradient descent)