python機器學習——自適應線性神經元

上篇博客咱們說了感知器,這篇博客主要記錄自適應線性神經元的實現算法及一些其餘的訓練細節,自適應線性神經元(簡稱爲Adaline)由Bernard Widrow和他的博士生Tedd Hoff提出,對感知器算法進行了改進。python

固然Adaline對輸入向量x的處理和感知器是同樣的,都是使用一個權重向量w與x線性組合後獲得z,再使用函數將z壓縮到二元輸入(1/-1),區別在於Adaline使用梯度降低法來更新w。算法

由於咱們的目的是準確分類,那麼咱們須要來衡量分類效果的好壞,在這裏咱們介紹目標函數: $$ J(w) = \frac12 \sum_i^n(y^i - \phi(z^i))^2 $$ 它也能夠叫作損失函數,經過上式咱們能夠大體理解爲何叫作損失函數,此函數能夠計算出全部訓練樣本的真實值和預測值之間的偏差平方和(Sum of Squared Errors,簡稱SSE),式子前面的那個1/2是爲了以後求導方便添加的,沒有其餘意義。app

有了損失函數,因而咱們的目的更具體一點,就是爲了選擇合適的w,使損失函數取得最小值,損失函數越小,就意味着錯誤分類的狀況越少,算法的分類效果也就越好。而由於Adaline的損失函數是一個凸函數,因此咱們可使用梯度降低來找到使損失函數取值最小的權重向量w,咱們能夠想象爲一個小球滾下山:函數

剛開始的w也許會獲得一個很大的損失函數,可是因爲損失函數J是w的函數,而且也是一個凸函數,它存在一個最小值,學過微積分的朋友應該知道,要找到一個函數的最值,通常的方法經過求導並使導數爲零,解出的那個x就是最值,在這裏的梯度降低也就是求導,但因爲w是一個權重向量,是多維的,因此須要損失函數對w求偏導,獲得w中每一個份量的偏導數,而後再更新整個w,具體的推導過程以下: $$ 注意:w爲向量,w_j爲向量w中的某一份量\w = w + \Delta w\\Delta w = -\eta\Delta J(w)\\frac{\partial J}{\partial w_j} = \frac{\partial }{\partial w_j} \frac 12 \sum_i(y^i-\phi(z^i))^2 \= \frac 12 \frac{\partial }{\partial w_j} \sum_i(y^i-\phi(z^i))^2 \= \frac 12 \sum_i2(y^i-\phi(z^i)) \frac{\partial }{\partial w_j}(y^i-\phi(z^i))\= \sum_i(y^i-\phi(z^i))\frac{\partial }{\partial w_j}(y^i-\sum_i(w_j^ix_j^i))\=\sum_i(y^i-\phi(z^i))(-x_j^i)\=-\sum_i(y^i-\phi(z^i))x_j^i\因此\Delta w_j = -\eta\frac{\partial J}{\partial w_j}=\eta\sum_i(y^i-\phi(z^i))x_j^i $$ 一點要注意因此的權重向量w中的份量是同時更新的,並且每次更新都用到了全部的訓練樣本,因此梯度降低法也被稱爲批量梯度降低(batch gradient descent)學習

接下來咱們具體來實現自適應線性神經元,因爲和感知機的學習規則很類似,因此直接在感知器的基礎上進行修改獲得,其中須要修改fit方法,由於在這裏咱們要使用梯度降低算法。.net

class AdalineGD(object):
    """ADAptive LInear NEuron classifier.

    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 miscalssifications in every epoch.

    """

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

    def fit(self, X, y):
        """Fit training data.

        :param X:{array-like}, shape=[n_samples, n_features]
        Training vectors,
        :param y: array-like, shape=[n_samples]
        Target values.
        :return:
        self:object

        """

        self.w_ = np.zeros(1 + X.shape[1]) # Add w_0
        self.cost_ = []

        for i in range(self.n_iter):
            output = self.net_input(X)
            errors = (y - output)
            self.w_[1:] += self.eta * X.T.dot(errors)
            self.w_[0] += self.eta * errors.sum()
            cost = (errors ** 2).sum() / 2.0
            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 activation(self, X):
        """Computer linear activation"""
        return self.net_input(X)
    
    def predict(self, X):
        """Return class label after unit step"""
        return np.where(self.activation(X) >= 0.0, 1, -1)

分別使用不一樣的學習率(0.01和0.0001)訓練,觀察神經元學習過程。其中學習率、迭代次數咱們稱他們爲超參數(hyperparameters),咱們能夠手動設置,超參數設置的是否合適對於整個訓練過程都很重要。code

fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(8,4))
ada1 = AdalineGD(n_iter=10, eta=0.01).fit(X, y)
ax[0].plot(range(1, len(ada1.cost_) + 1), np.log10(ada1.cost_), marker='o')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('log(Sum-squared-error)')
ax[0].set_title('Adaline - Learning rate 0.01')
ada2 = AdalineGD(n_iter=10, eta=0.0001).fit(X, y)
ax[1].plot(range(1, len(ada2.cost_) + 1), ada2.cost_, marker='o')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Sum-squared-error')
ax[1].set_title('Adaline - Learning rate 0.0001')
plt.show()

能夠看出,左圖中學習率爲0.01,隨着迭代次數的增長,偏差在增長,說明學習率設置的不合適,產生了很大的危害,而右圖學習率爲0.0001,隨着迭代次數的增長,偏差在減小,可是減小的過於緩慢,算法收斂的很慢,訓練的效率過低,因此咱們能夠看出過大或太小的學習率都是不合適的。blog

由右圖能夠看出,若是學習率過大,就會致使每次梯度降低時都跳過了對應最小值的權重向量w,使得算法沒法收斂。ci

接下來咱們介紹一種數據預處理方法,在訓練前將特徵進行某種縮放操做,這裏咱們稱爲特徵標準化,可使全部特徵數據縮放成平均值爲0,方差爲1,加快模型的訓練速度,並且能夠避免模型學習的很扭曲。get

具體公式以下: $$ x_j^, = \frac {x_j-\mu_j}{\sigma_j} $$ 具體實現以下:

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()

數據已經預處理結束,接下來咱們開始訓練模型

ada = AdalineGD(n_iter=15, eta=0.01)
ada.fit(X_std, y)
plot_decision_region(X_std, y, classifier=ada)
plt.title('Adaline - Gradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc='upper left')
plt.show()

plt.plot(range(1, len(ada.cost_) + 1), ada.cost_, marker='o')
plt.xlabel('Epoches')
plt.ylabel('Sum-squared_error')
plt.show()

從上圖看出,隨着迭代次數的增長,偏差逐漸下降,雖然學習率爲0.01,在進行標準化以前,算法並不能收斂,但通過標準化後,算法最終收斂。

相關文章
相關標籤/搜索