机器学习第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

发布了57 篇原创文章 · 获赞 1690 · 访问量 76万+

猜你喜欢

转载自blog.csdn.net/u013733326/article/details/78863082