菜鳥筆記Python3——機器學習(一) :梯度下降模型

參考資料

<PYTHON_MACHINE_LEARNING> chapter2
Training Machine Learning Algorithms for Classifcation

引言:

在學習過基本的單層感知機模型之后,我們現在來學習了解另一種單層神經網絡模型:
ADAptive LInear NEuron (Adaline) 自適應線性神經單元 這種 Adaline 的特別之處在于 它引入了損失函數 cost function
Adaline 與 Perceptron 的根本區別在于它們對權值更新方法的不同
***the weights are updated based on a linear activation function rather than a unit step function ***
Perceptron (感知機):


Adaline (自適應線性) J是損失函數

展開形式如下

......

算法原理

1:算法流程

自適應線性單元 Adaline



感知機 Perceptron


Adaline 比 perceptron 多了一個量化器,用于將激勵函數輸出的結果進行二分,來判斷樣本的類別

2: 損失函數

損失函數是用來衡量模擬結果與實際結果差異的一個函數
我們的思路是,選擇一個方法使得損失函數趨向最小值,那么這也就說明了我們的模型對實際符合的越好,根據這一個原理去更新權值
思路圖:

在 Adaline 中,我們選擇 SSE 平方差函數作為損失函數

由于 Adaline 采用的線性激勵函數 (本次練習采用的是恒同映射) 是一個可微的函數(differentiable), 所以我們可以定義 J 的梯度(偏分)


把這個式子用矩陣表達一下~


再根據這個梯度更新權值


分類根據每一次迭代使用的訓練集范圍不同,梯度下降算法可以分成三種: 批量梯度下降(BGD),隨機梯度下降(SGD),小批量梯度下降(MBGD)

  • 批量梯度下降(BGD):損失函數由全部訓練集的數據誤差構成,當數據量很大的時候,速度會非常慢
  • 隨機梯度下降(SGD): 每一次更新只考慮一個樣本的數據誤差,所以速度很快,能進行在線的參數更新
  • 小批量梯度下降(MBGD): 每次跟新只考慮小批量的樣本數據誤差, 是前兩種策略的折中方案

Python 實現(BGD)

有了第一節的基礎,這個實現起來就比較容易了,我們新建一個Adaline類,這個類與Perceptron 類的區別在于更新權值的方法不同,一樣,需要注釋的地方都在代碼里面了,直接看代碼

__author__ = 'Administrator'
#! /usr/bin/python <br> # -*- coding:utf8 -*-
import numpy as np
class AdalineGD(object):
    """
    ADAlineGD 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 misclassifications 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.
    Parameters
    ----------
    X : {array-like}, shape = [n_samples, n_features] X的形式是列表的列表
    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])
        #X.shape = (100,2),zeros 生成的是列向量
        #self.w_ 是一個(3,1)的矩陣
        # print('X.shape[1]=',X.shape[1])
        self.cost_ =[]
        #self.cost_損失函數 cost_function
        # zeros()創建了一個 長度為 1+X.shape[1] = 1+n_features 的 0數組
        # self.w_ 權向量
        self.errors_ = []
        for i in range(self.n_iter):
            output = self.net_input(X)
            '''
            if i==1:
                print(output)
                print(y)
            '''
            # y(100,1) output(100,1),errors(100,1)
            errors = (y - output)

            self.w_[1:] += self.eta * X.T.dot(errors)
            #   X先取轉置(2,100),再矩陣乘法乘以 errors(100,1) X.T.dot(errors) (2,1)
            self.w_[0] += self.eta * errors.sum()
            cost = (errors**2).sum()/2
            self.cost_.append(cost)
        # print(self.w_.shape)
        # print(self.w_)
        # print(X.shape)
        return self

    def net_input(self, X):
        """Calculate net input"""
        #np.dot(A,B)表示矩陣乘法 ,X(100,2) self.w_[1:](2,1)
        #注意 這里每一組 向量x = [x1,x2] 不是 [x1,,,,,,x100]!!!
        #所以得到的 net_input 是(100,1)的矩陣 表示100個樣本的net_input
        return (np.dot(X, self.w_[1:])+self.w_[0])

    def activation(self,X):
        """Compute linear activation"""

        return self.net_input(X)

    def predict(self, X):
        """return class label after unit step"""
        return np.where(self.net_input(X) >= 0.0, 1, -1)




然后還是用鳶尾花Iris的數據集來測試一下

from GD import AdalineGD
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from PDC import plot_decision_regions
filename = 'Iris.csv'
df = pd.read_csv(filename,header=None)
y = df.iloc[0:100, 4].values # .values將dataframe中的值存進一個list中
y = np.where(y=='Iris-setosa',-1,1) #如果是 Iris-setosa y=-1否則就是1 (二元分類)
X = df.iloc[0:100,[0,2]].values

fig, ax = plt.subplots(nrows=1, ncols=2,figsize=(10, 6))
adal = AdalineGD(n_iter=10,eta=0.01).fit(X,y)

ax[0].plot(range(1,len(adal.cost_)+1),
           np.log10(adal.cost_),marker='o')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('log(Sum-squared-error)')
ax[0].set_title('Adaline - Learing rate 0.01')
ada2 = AdalineGD(n_iter=10,eta=0.0001).fit(X,y)
ax[1].plot(range(1,len(ada2.cost_)+1),
           np.log10(ada2.cost_),marker='o')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('log(Sum-squared-error)')
ax[1].set_title('Adaline - Learing rate 0.0001')
plt.savefig('Compare effect.png')
plt.show()

結果如圖

我們可以發現當學習速率過快時,有可能會讓誤差發散
這是因為
這個時候的學習速率(dw)已經超過了 |Wmin-Winitial|



但是,學習速率過小又會導致收斂速率過慢,于是我們要引入歸一化/標準化處理,讓各個數據的維度處在一個級別

歸一化/標準化處理

常用的歸一化處理方法是

減去平均值再除以標準差

#接上面的代碼
X_std = np.copy(X)
X_std[:,0] = (X[:,0]-X[:,0].mean())/ X[:,0].std() #.std() 標準差
X_std[:,1] = (X[:,1]-X[:,1].mean())/ X[:,1].std() #.std() 標準差

ada = AdalineGD(n_iter=15, eta=0.01)
ada.fit(X_std,y)
print(ada.predict(X_std))
plot_decision_regions(X_std, y, classifier=ada)
plt.title('Adaline- Dradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc='upper left')
plt.savefig('Adaline- Dradient Descent.png')
plt.show()
plt.plot(range(1,len(ada.cost_)+1), ada.cost_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Sum-squared-error')
plt.savefig('Sum-squared-error.png')
plt.show()

看一下結果


剛剛對學習速率是 0.01 發散的,現在收斂了


學習的成果 ~ ~ ~~~
我們像第一節一樣換一下訓練集然后測試一下

Adaline- Dradient Descent.png

OK!

PS : 有一個問題沒有想通,對于激勵信號是階躍函數,我們定義了 W0 應該是閾值,這個閾值應該是固定,但是很顯然在模型中我們依然把它當作了未知數,對于激勵信號是線性函數的,閾值的作用,,,,,暫時我還沒有想明白

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容