機器學習第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.8284271247461903
第 181 次調整,當前權值: [-23.17488429 -6.24866824 5.51195715] ,當前誤差信號值: 2.8284271247461903
第 182 次調整,當前權值: [-23.21836255 -6.07720202 5.8968621 ] ,當前誤差信號值: 3.4641016151377544
第 183 次調整,當前權值: [-23.30475604 -7.11336413 3.47840412] ,當前誤差信號值: 2.8284271247461903
第 184 次調整,當前權值: [-23.30475604 -7.11336413 3.47840412] ,當前誤差信號值: 0.0
當前誤差信號值小於最小的誤差信號值,已收斂,訓練完成,程序退出.
[35,68]被劃分爲負類
[35,82]被劃分爲正類