【机器学习】--感知机

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shankezh/article/details/78906271

简单介绍:

     感知机是二分类模型,和其它二分类比起来,最大区别是输出类别是{1,-1}。


算法特点:

     1、感知机属于有监督学习的一种。
     2、感知机利用具有正负类别的示例数据训练出判别模型。
     3、比起逻辑回归,简单许多。
     4、具有原始形式和对偶形式。
     5、单纯使用感知机,需要对数据做认知处理,因为选择的超平面方程会直接影响到训练结果,例如不能使用直线方程去拟合曲线方程更适合的数据。
     6、最优化问题,常见三板斧,这里用梯度下降。
     7、是神经网络和支持向量机的基础,其实就是个激活函数。


学习感知机的基本流程:

     1、了解基础知识。
     2、了解使用了什么样的损失函数
     3、推导(如果你推过LR,NN,SVM之类的,这个就显得太简单了)
     4、写代码看效果


基础知识:

感知机模型:

定义
        假设输入空间(特征向量)为X⊆Rn,输出空间为Y={-1, +1}。输入x∈X表示实例的特征向量,对应于输入空间的点;输出y∈Y表示示例的类别。由输入空间到输出空间的函数为:f(x)=sign(w·x + b)称为感知机。其中,参数w叫做权值向量,b称为偏置。w·x表示w和x的内积。sign为符号函数,即:

      



几何解释    

        感知机模型是线性分类模型,感知机模型的假设空间是定义在特征空间中的所有线性分类模型,即函数集合{f|f(x)=w·x+b}。线性方程 w·x+b=0对应于特征空间Rn中的一个超平面S,其中w是超平面的法向量,b是超平面的截踞。这个超平面把特征空间划分为两部分。位于两侧的点分别为正负两类。超平面S称为分离超平面,如下图:

感知机损失函数:

经验风险函数

        给定数据集T={(x1,y1),(x2,y2)...(xN,yN)}(其中xi∈X=Rn,yi∈Y={-1, +1},i=1,2...N),感知机sign(w·x+b)学习的损失函数定义为

其中M为误分类点的集合,这个损失函数就是感知机学习的经验风险函数。                           
    显然,损失函数L(w,b)是非负的。如果没有误分类点,那么L(w,b)为0,误分类点数越少,L(w,b)值越小。一个特定的损失函数:在误分类时是参数w,b的线性函数,在正确分类时,是0.因此,给定训练数据集T,损失函数L(w,b)是w,b的连续可导函数。


如果你有心了解感知机的学习策略,你可能会看到L2范数

L2范数 :

L2范数是我们最常见最常用的范数了,我们用的最多的度量距离欧氏距离就是一种L2范数,它的定义如下:

表示向量元素的平方和再开平方。 


像L1范数一样,L2也可以度量两个向量间的差异,如平方差和(Sum of Squared Difference):

对于L2范数,它的优化问题如下: 

L2范数通常会被用来做优化目标函数的正则化项,防止模型为了迎合训练集而过于复杂造成过拟合的情况,从而提高模型的泛化能力.

推导:

     由于误差函数:

分别对w和b求导,得出结果:

所以,根据梯度下降,我们的w,b的求解方法就是随机选取一个误分类点(xi,yi)进行更新,其中η是学习速率:

程序部署步骤:

1、获取数据

2、初始化参数

3、定义预测函数

4、定义误差函数

5、模型训练函数

6、画图

数据源:

本次使用到的数据是之前做逻辑回归用的,请存储为logistic_d1_s.txt即可:

-0.017612  14.053064  0
-1.395634  4.662541    1
-0.752157  6.538620    0
-1.322371  7.152853    0
0.423363    11.054677  0
0.406704    7.067335    1
0.667394    12.741452  0
-2.460150  6.866805    1
0.569411    9.548755    0
-0.026632  10.427743  0
0.850433    6.920334    1
1.347183    13.175500  0
1.176813    3.167020    1
-1.781871  9.097953    0
-0.566606  5.749003    1
0.931635    1.589505    1
-0.024205  6.151823    1
-0.036453  2.690988    1
-0.196949  0.444165    1
1.014459    5.754399    1
1.985298    3.230619    1
-1.693453  -0.557540  1
-0.576525  11.778922  0
-0.346811  -1.678730  1
-2.124484  2.672471    1
1.217916    9.597015    0
-0.733928  9.098687    0
-3.642001  -1.618087  1
0.315985    3.523953    1
1.416614    9.619232    0
-0.386323  3.989286    1
0.556921    8.294984    1
1.224863    11.587360  0
-1.347803  -2.406051  1
1.196604    4.951851    1
0.275221    9.543647    0
0.470575    9.332488    0
-1.889567  9.542662    0
-1.527893  12.150579  0
-1.185247  11.309318  0
-0.445678  3.297303    1
1.042222    6.105155    1
-0.618787  10.320986  0
1.152083    0.548467    1
0.828534    2.676045    1
-1.237728  10.549033  0
-0.683565  -2.166125  1
0.229456    5.921938    1
-0.959885  11.555336  0
0.492911    10.993324  0
0.184992    8.721488    0
-0.355715  10.325976  0
-0.397822  8.058397    0
0.824839    13.730343  0
1.507278    5.027866    1
0.099671    6.835839    1
-0.344008  10.717485  0
1.785928    7.718645    1
-0.918801  11.560217  0
-0.364009  4.747300    1
-0.841722  4.119083    1
0.490426    1.960539    1
-0.007194  9.075792    0
0.356107    12.447863  0
0.342578    12.281162  0
-0.810823  -1.466018  1
2.530777    6.476801    1
1.296683    11.607559  0
0.475487    12.040035  0
-0.783277  11.009725  0
0.074798    11.023650  0
-1.337472  0.468339    1
-0.102781  13.763651  0
-0.147324  2.874846    1
0.518389    9.887035    0
1.015399    7.571882    0
-1.658086  -0.027255  1
1.319944    2.171228    1
2.056216    5.019981    1
-0.851633  4.375691    1
-1.510047  6.061992    0
-1.076637  -3.181888  1
1.821096    10.283990  0
3.010150    8.401766    1
-1.099458  1.688274    1
-0.834872  -1.733869  1
-0.846637  3.849075    1
1.400102    12.628781  0
1.752842    5.468166    1
0.078557    0.059736    1
0.089392    -0.715300  1
1.825662    12.693808  0
0.197445    9.744638    0
0.126117    0.922311    1
-0.679797  1.220530    1
0.677983    2.556666    1
0.761349    10.693862  0
-2.168791  0.143632    1
1.388610    9.341997    0
0.317029    14.739025  0

代码部分:

由于感知机太过简单,而且不小心写成了一个类,就一次粘贴全部代码:

#!/usr/bin/env python
# encoding: utf-8

"""
@version: v1.0
@author: Jason Zhu
@license: Apache Licence
@file: 感知机.py
@time: 2017-12-19 18:57
"""

import numpy as np
from matplotlib import pyplot as plt

class Perceptron():
    #权重
    weight = []

    #截距
    b = []

    #损失值
    loss = []

    #样本集合和结果
    train_x = []
    train_y = []

    #学习速率
    learning_rate = 0.1

    #错误分类点
    err_classify_point = []

    def get_data(self):
        x = []
        y = []
        fileIn = open('logistic_d1_s.txt')
        # print(fileIn.readline().strip().split(' |  |  |    |\r\n'))
        for line in fileIn.readlines():
            linearr = line.strip().split()
            x.append([float(linearr[0]), float(linearr[1])])
            if 0. == float(linearr[2]):
                y.append(-1)
            else:
                y.append(float(linearr[2]))
        self.train_x = np.array(x)
        self.train_y = np.mat(y).T

    # 定义符号函数,z = wT * xT + b
    def sign(self,z):
        npy = []
        for i in range(len(z)):
            if z[i][0] >= 0:
                npy.append([1])
            else:
                npy.append([-1])
        return npy

    def set_param(self):
        m, n = np.shape(self.train_x)
        self.weight = np.zeros((n,1))  #初始化权重矩阵
        self.b = 0

    def predict(self,x):
        return self.sign(x)

    def loss_func(self):
        # z = np.dot(self.train_x, self.weight) + self.b
        # self.loss = -self.train_y.T * (z)
        loss_sum = 0.0
        for i in self.err_classify_point:
            z = self.weight[0,0] * self.train_x[i,0] + self.weight[1,0] * self.train_x[i,1] + self.b[0,0]
            loss_sum = loss_sum + self.train_y[i] * z
        self.loss.append(- loss_sum)
        # print( "损失为:" +  str( loss_sum) )

    def train(self,iterMax = 100):
        # print("go")
        # print(np.shape(self.weight))
        for i in range(iterMax):
            #z = wx + b
            z = np.dot(self.train_x,self.weight) + self.b
            predict_y = self.sign(z)

            err_predict_index = []
            self.err_classify_point = []
            #找到所有误分类点的序号
            for m in range(np.size(self.train_y)):
                if self.train_y[m] != predict_y[m]:
                    err_predict_index.append(m)
                    self.err_classify_point.append(m)

            #随机选择一个误分类点
            err_point = np.random.randint(0,len(err_predict_index))

            # print((self.train_y[err_predict_index[err_point]]),predict_y[err_predict_index[err_point]])

            #对误分类点进行梯度下降,np.mat(self.train_x[err_predict_index[err_point]])需要将其格式化成1x2
            aLw = -np.dot(np.mat(self.train_x[err_predict_index[err_point]]).T,self.train_y[err_predict_index[err_point]])
            aLb = -self.train_y[err_predict_index[err_point]]

            self.weight = self.weight - self.learning_rate * aLw
            self.b = self.b - self.learning_rate * aLb

            # print("weight:" + str(self.weight) + "\n bias:" + str(self.b))
            self.loss_func()

    def show_pic(self):
        #根据结果绘制点集
        plt.figure(1)
        for i in range(len(self.train_x)):
            if self.train_y[i][0] == 1:
                plt.plot(self.train_x[i,0], self.train_x[i,1], color='red', marker='*')
            else:
                plt.plot(self.train_x[i, 0], self.train_x[i, 1], color='blue', marker='*')
        x1 = np.arange(-3,3,0.1).tolist()
        print(self.err_classify_point)
        #查看上一次误分类点
        # for t in self.err_classify_point:
        #    plt.plot(self.train_x[t,0],self.train_x[t,1],color='brown',marker='o')

        x2 = []
        for m in range(len(x1)):
            xx2 = -(self.weight[0,0] *x1[m] + self.b[0,0] ) / self.weight[1,0]
            x2.append(xx2)
        plt.plot(x1, x2, color='black')
        plt.show()

if __name__ == "__main__":
    np.set_printoptions(suppress=True)  # 不使用科学计数法显示结果
    m = Perceptron()
    m.get_data()
    m.set_param()
    m.train(2000)

    print("初次损失为:" + str(m.loss[0]))
    print("最终损失为:" + str(m.loss[-1]))
    print("权重w" + str(m.weight) + "\n"
          +"截距b" + str(m.b) + "\n"
          )
    print("上一次误分类点数量:" + str(  len(m.err_classify_point)))
    m.show_pic()

运行之后,看一下结果:


结果如图:

写在最后:

  写代码的时候也参考了很多blog,如果细心的人也会发现,梯度下降的代码会有区别,原因大多是选择的损失函数不一样,就我目前已经见到过用平方差误差函数,和最大似然误差函数两种了,我这里选用经验风险函数,原因是因为参考了<统计学习方法>李航这本书,关于误差函数的选择,有网上有专门的讲解,不过一般选择最推荐的,毕竟都是前辈推出来的经验结论。

参考链接:

【2】<<统计学习方法>> 作者:李航


猜你喜欢

转载自blog.csdn.net/shankezh/article/details/78906271