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

參考資料

<PYTHON_MACHINE_LEARNING> chapter2
Training Ma
chine Learning Algorithms for Classifcation

引言

在上一節,我們學習了梯度下降算法,現在我們來了解一下梯度下降算法的一個類型——隨機梯度下降,每一次更新只考慮一個樣本的數據誤差,所以速度很快,能進行在線的參數更新... ...

原理

基本原理與批量梯度下降算法相同,不同的是更新權值的方法
這是上一章節的權值更新方法


在隨機梯度下降中,我們一次考慮一個樣本的誤差,再逐個加和,一旦到達最小,就可以停下來,所以可以大大加快模擬的速度,同時每一次迭代開始的時候,我們都打亂一遍訓練集,為了減小樣本之間造成的參數更新抵消問題

Python實現

我們在上一節AdalineGD類的基礎上稍加改動就得到了AdalineSGD
主要的改動有:

  • 增加了一個洗牌(shuffle)方法,用于在每一次迭代開始的時候打亂訓練集
  • 增加了一個權值更新(._update_weights)方法, 用于更新每一個樣本的誤差

ok
上代碼吧, 有什么問題都在注釋里說明了

__author__ = 'Administrator'
#! /usr/bin/python <br> # -*- coding:utf8 -*-
import numpy as np
from numpy.random import seed
class AdalineSGD(object):
    """
    ADAlineSGD 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(時間起點).
    shuffle: bool (default :True) 洗牌
    每次訓練開始的時候將訓練集重新打亂
    如果為真,阻止循環
    用隨機數來 打亂數據集 并且初始化權值
    """

    def __init__(self, eta=0.01, n_iter=10,shuffle = True,
                 random_state = None):
        self.eta = eta
        self.n_iter = n_iter
        self.w_initialiezed = False
        self.shuffle = shuffle
        if random_state:
            seed(random_state)

    def _shuffle(self, X, y):
        """shuffle training data"""
        r = np.random.permutation(len(y))
        #random.permutation(array/number)
        #接受array的時候返回的是 array 的打亂的排列 ,
        # 接受 數字的時候 返回一個 range(number) 的打亂的排列
        # 所謂‘排列’可以理解成一個有數組序號組成的數組
        #X[r] 就是 X 按照 r 的排列重排的數組(只重排第一個索引(重排行)),
        # 同時 X[r] 返回一個copy 的對象
        #X 本身并沒有改變
        #random.shuffle(array)不返回對象,重排array,改變array本身
        return X[r], y[r]

    def _initialize_weights(self, m):
        """Initialize weights to zeros"""
        self.w_ = np.zeros(1+m)
        #初始化之后把屬性 w_initialiezd 設為 True
        self.w_initialiezed = True

    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._initialize_weights(X.shape[1])
        #將 權值屬性self.w_初始化 (100,1)的0數組
        #X.shape = (100,2)
        self.cost_ =[]
        #self.cost_損失函數 cost_function
        for i in range(self.n_iter):
            #開始默認 shuffle為真
            if self.shuffle:
                X, y = self._shuffle(X,y)
            cost = []
            for xi,target in zip(X,y):
                cost.append(self._update_weights(xi,target))
            avg_cost = sum(cost)/len(y)
            #avg_cost = np.array(cost).mean()
            #也可用這種方法求平均數
            self.cost_.append(avg_cost)
        return self

    def partial_fit(self, X, y):
        """Fit training data  without reinitializing the weights"""
        if not self.w_initialiezed:
            self._initialize_weights(X.shape[1])
        if y.ravel().shape[0] > 1:
            for xi, target in zip(X, y):
                self._update_weights(xi,target)
        else:
            self._update_weights(X, y)
        return self




    def _update_weights(self, xi, target):
        """Apply Adaline learning rule to update the weights"""
        output = self.net_input(xi)
        error = (target - output)
        #每次error都是一個float
        #xi對應一個樣本 (2,1)
        #wi 的 update = eta* xi[i]*error
        self.w_[1:] += self.eta * xi.dot(error)
        self.w_[0] += self.eta * error
        cost = 0.5 * error ** 2
        return cost

    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 的數據集來驗證一下

__author__ = 'Administrator'
#!/usr/bin/python <br> # -*- coding: utf8 -*-
from GDR import AdalineSGD
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
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 = AdalineSGD(n_iter=15, eta=0.01,random_state=1)
ada.fit(X_std, y)

plot_decision_regions(X_std, y, classifier=ada)
plt.title('Adaline - Stochastic Gradient Descent')
plt.xlabel('sepal length [standardized]')
plt.ylabel('petal length [standardized]')
plt.legend(loc='upper left')
plt.savefig('Adaline- Stochastic Gradient Descent.png')
plt.show()
plt.plot(range(1,len(ada.cost_)+1), ada.cost_, marker='o')
plt.xlabel('Epoches')
plt.ylabel('Average Cost')
plt.savefig('Average Cost - Epoches.png')
plt.show()

比較一下達到最優的速率
SGD



BGD



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

推薦閱讀更多精彩內容