机器学习第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]被划分为正类