機器學習第5章第4節 : 基於梯度下降的線性分類器

機器學習第5章第4節 : 基於梯度下降的線性分類器

概述

梯度

梯度是一個向量場,標量場中某一點上的梯度指向標量場增長最快的方向,梯度是這個的最大變化率

梯度下降

梯度下降,就是使用負梯度方向來決定每次迭代的新的搜索方向,從而使得在每次迭代過程中,都能讓待優化的目標函數逐步減小,梯度下降法使用的是二範數下的最速下降法,最速下降法的簡單形式如下:

x( k + 1 ) = x( k ) - a * g( k )

其中,a是學習速率,也可以是較小的常數。 g( k ) 是x( k ) 的梯度。

機器學習算法效果究競如何。或者說誤差爲多少,可以用誤差函數J(θ)來度量。其中的參數θ在神經網絡中可以理解爲權值。如何調整權值θ以使得J(θ)的取得最小值有很多種方法,梯度下降法是按下面的步驟進行的:

1) 對θ賦值,這個值可以是隨機的,也可以讓θ是一個全零的向量

2) 改變θ的值,使得J(θ)按梯度下降的方向進行減少。

爲了更清楚的表達,我們來看一下誤差曲面及梯度下降圖。

誤差曲面及梯度下降圖

在上圖中,它表示了參數θ與誤差函數J(θ)的關係,也稱誤差曲面圖,該圖上的方向表明,隨着迭代次數的增加,誤差走向了曲面的最小誤差點

紅色較爲凸起的部分是表示J(θ)有着比較高的取值,對其進行神經網絡訓練的時候,最完美的目標是:讓J(θ)的值儘量降低,降到最低處,也就是最凹的部分。

梯度下降法的第一步是給θ一個初值,假設隨機給的初值是在最高的十字點處: 然後將θ按照梯度下降的方向進行調整,就會使得J(θ)往更低的方向變化。算法的結束將是在θ下降到無法繼續下降爲止。當然,可能梯度下降的最終點並非是全局最小點而是一個局部最小點,如下圖:

局部最小點

上圖的情況在神經網絡訓練中是要儘量避免出現的,這裏的梯度下降到局部最小點後停止,神經網絡在一個不正確的位置收斂了,訓練在這裏停止,此時,再繼續訓練也沒有任何的效果。

數學知識

需要了解的數學知識有:
1) 導數的幾何意義
2) 積分的幾何意義
3) 微分的幾何意義
4) 偏導數
5) 梯度

代碼

# -*- coding: utf-8 -*-
"""

@author: Oscar

梯度下降算法實現分類
"""

import numpy as np
import math
import matplotlib.pyplot as plt

#編寫神經網絡模塊
class Mplannliner:
    def __init__(self):
        #學習率的初始值
        self.learn_speed_start = 0.1
        #學習率
        self.learn_speed = 0.0
        #偏置
        self.b = 1
        #最小誤差精度
        self.min_error_signal = 0.05
        #衰減因子
        self.r = 5.0
        #學習次數
        self.train_count = 100
        #測試集
        self.test_point = []
    #初始化測試集
    def test_point_init(self):
        self.test_point = []
    #初始化最小誤差精度
    def min_error_signal_init(self,min_error_signal):
        self.min_error_signal = min_error_signal
    #初始化樣本
    def samples_init(self,samples):
        #學習的數據
        train_data = []
        #期望輸出,對應的是學習數據的分類
        classify = []
        #訓練的權值
        weight = [self.b]
        #構造訓練數據
        #[[[9,25],-1],[[5,8],-1],[[15,31],-1],[[35,62],-1],[[19,40],-1],[[28,65],1],[[20,59],1],[[9,41],1],[[12,60],1],[[2,37],1]]
        for smp in samples:
            #訓練所需要的數據
            train_data.append([1] + smp[0])
            #對應訓練數據的分類
            classify.append(smp[1])
        #初始化權值
        for i in range( len(train_data[0]) - 1 ):
            weight.append(0.0)
        #把數據添加到self
        self.train_data = np.array(train_data)
        self.classify = np.array(classify)
        self.weight = np.array(weight)
    #初始化學習率的初始值
    def learn_speed_start_init(self,init_learn_speed_start):
        self.learn_speed_start = init_learn_speed_start
    #初始化衰減因子
    def r_init(self,init_r):
        self.r = init_r
    #初始化學習次數
    def train_count_init(self,init_train_count):
        self.train_count = init_train_count
    #感知器
    def sgn(self,v):
        if v > 0 :
            return 1
        else:
            return -1
    #拿到感知器的返回值
    def get_sgn(self,current_weight,current_train_data):
        return self.sgn(np.dot(current_weight.T,current_train_data))
    #獲取誤差信號值
    def get_error_signal(self,current_weight,current_train_data,current_classify):
        return current_classify - self.get_sgn(current_weight,current_train_data)
    #更新權值
    def update_weight(self,old_weight,current_train_data,current_classify,current_learn_speed,current_train_count):
        #獲取誤差信號值
        current_error_signal = self.get_error_signal(old_weight,current_train_data,current_classify)
        #更新學習速率
        self.learn_speed  = self.learn_speed_start / ( 1 + (current_train_count / float(self.r)))
        new_weight = old_weight + (current_learn_speed * current_error_signal * current_train_data)
        return new_weight
    #訓練
    def train(self):
        current_count = 0
        while True:
            error_signal = 0
            i = 0
            for xn in self.classify:
                current_error_signal = self.get_error_signal(self.weight,self.train_data[i],xn)
                self.weight = self.update_weight(self.weight,self.train_data[i],xn,self.learn_speed,current_count)
                i += 1
                error_signal += math.pow(current_error_signal,2)
            error_signal = math.sqrt(error_signal)
            current_count += 1

            print("第",current_count,"次調整,當前權值:",self.weight,",當前誤差信號值:",error_signal)
            #判斷是否退出:
            if abs(error_signal) < self.min_error_signal:
                print("當前誤差信號值小於最小的誤差信號值,已收斂,訓練完成,程序退出.")
                break
            if current_count > self.train_count:
                print("當前訓練次數已經達到設定的最大訓練值,程序退出.")
                break
    #對測試數據進行分類
    def simulate(self,test_data):
        if self.get_sgn(self.weight,np.array([1] + test_data)) > 0:
            return 1
        else:
            return -1
    #添加測試點的數據
    def add_draw_point(self,point):
        self.test_point.append(point)
    #繪製可視化圖
    def draw2d(self):
        points_x = []
        points_y = []
        i = 0
        #繪製訓練集
        for smp in self.train_data:
            points_x.append(smp[1])
            points_y.append(smp[2])
            if self.classify[i] > 0:
                #正類劃分爲綠色小圓圈
                plt.plot(smp[1],smp[2],"og")
            else:
                #負類劃分爲紅色色小圓圈
                plt.plot(smp[1],smp[2],"or")
            i += 1
        test_point_x = []
        test_point_y = []
        #繪製測試集
        for testpoint in self.test_point:
            if self.simulate(testpoint) == 1:
                #測試集,正類爲綠色星號
                plt.plot(testpoint[0],testpoint[1],"*g")
            else:
                #測試集,負類爲紅色色星號
                plt.plot(testpoint[0],testpoint[1],"*r")
            test_point_x.append(testpoint[0])
            test_point_y.append(testpoint[1])
        #設置兩軸的最大最小值
        x_max = max(max(points_x),max(test_point_x)) + 5
        x_min = min(min(points_x),min(test_point_x))
        y_max = max(max(points_y),max(test_point_y)) + 5
        y_min = min(min(points_y),min(test_point_y))
        if x_min >0:
            x_min=0
        if y_min >0:
            y_min=0

        #開始繪製
        plt.xlabel(u"X")
        plt.xlim(x_min, x_max)
        plt.ylabel(u"Y")
        plt.ylim(y_min, y_max)
        plt.title("ANN-LINER[red:- green:+]\n TRAIN_DATA[  O  ]\n TEST_DATA[  *  ]")
        lp_x1 = [x_min, x_max]
        lp_x2 = []
        #準備繪製分類線
        myb=self.weight[0]
        myw1=self.weight[1]
        myw2=self.weight[2]
        myy=(-myb-myw1*lp_x1[0])/float(myw2)
        lp_x2.append(myy)
        myy=(-myb-myw1*lp_x1[1])/float(myw2)
        lp_x2.append(myy)
        plt.plot(lp_x1, lp_x2, 'b--')
        plt.show()

#----------------------------------------------------------------------------------------------------#

#訓練數據
train_data=[[[9,25],-1],[[5,8],-1],[[15,31],-1],[[35,62],-1],[[19,40],-1],[[28,65],1],[[20,59],1],[[9,41],1],[[12,60],1],[[2,37],1]]
#實例化編寫的神經網絡的類
myAnn = Mplannliner()
#樣本初始化
myAnn.samples_init(train_data)
#學習率初始化
myAnn.learn_speed_start_init(0.1)
#搜索時間常數初始化
myAnn.r_init(50)
#最大訓練次數
myAnn.train_count_init(500)
#期望最小誤差
myAnn.min_error_signal_init(0.05)
#開始訓練
myAnn.train()

#驗證1
simulate_result =  myAnn.simulate([35,68])
if simulate_result == 1:
    print("[35,68]被劃分爲正類")
else:
    print("[35,68]被劃分爲負類")
myAnn.add_draw_point([35,68])

#驗證2
simulate_result2 =  myAnn.simulate([35,82])
if simulate_result2 == 1:
    print("[35,82]被劃分爲正類")
else:
    print("[35,82]被劃分爲負類")
myAnn.add_draw_point([35,82])

#開始繪製
myAnn.draw2d()

運行結果


......

第 180 次調整,當前權值: [-23.17469443  -7.07304644   3.77757325] ,當前誤差信號值: 2.8284271247461903181 次調整,當前權值: [-23.17488429  -6.24866824   5.51195715] ,當前誤差信號值: 2.8284271247461903182 次調整,當前權值: [-23.21836255  -6.07720202   5.8968621 ] ,當前誤差信號值: 3.4641016151377544183 次調整,當前權值: [-23.30475604  -7.11336413   3.47840412] ,當前誤差信號值: 2.8284271247461903184 次調整,當前權值: [-23.30475604  -7.11336413   3.47840412] ,當前誤差信號值: 0.0
當前誤差信號值小於最小的誤差信號值,已收斂,訓練完成,程序退出.
[35,68]被劃分爲負類
[35,82]被劃分爲正類

myAnn

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章