统计学习笔记2:感知机学习算法的对偶形式及其python代码实现

1感知机算法的对偶形式

笔记1中已经详细叙述过感知机学习算法的原始形式,详情可见统计学习笔记1:感知机学习算法的原始形式

     对偶形式的基本想法是将w和b表示为实例x_i和标签y_i的线性组合的形式,通过求解其系数来间接求得w和b。学习过程中不断修改w和b,设修改n次,则w关于(x_i,y_i)的增量分别是\alpha_iy_ix_i\alpha_iy_i,这里\alpha_i=n_i\eta,所以最后学习得到的w,b可以分别表示为

w=\sum_{i=1}^N{\alpha _iy_ix_i}

b=\sum_{i=1}^N{\alpha _iy_i}

    故感知机算法的对偶形式实现需要以下步骤:

       (1) \alpha \gets 0,b\gets 0

       (2)在训练集中选取数据(x_i,y_i)

       (3)如果y_i\left( \sum_{j=1}^N{\alpha _jy_jx_j\cdot x_i+b} \right) \leqslant 0,则需要更新\alpha_i和b的值如下

\alpha _i\gets \alpha _i+\eta

b\gets b+\eta y_i

       一直迭代直至没有误分类数据。

      上文可以看出判断误分类的依据中存在x_j\cdot x_i的内积形式,方便起见,可以预先将训练集中各个实例之间的内积计算出来并以矩阵形式存储,这就是所谓的Gram矩阵

G=\left[ x_i\cdot x_j \right] _{N\times N}

2感知机学习算法对偶形式python代码实现

2.1 问题描述

       问题与笔记1一样来源于李航老师《统计学习方法》中的例2.2。正样本点为x_1=\left( 3,3 \right) ^T,x_2=\left( 4,3 \right) ^T,负样本点是x_3=\left( 1,1 \right) ^T,试用感知机学习算法对偶形式求感知机模型。

2.2数据导入

        在数据导入的过程中可以顺便计算出Gram矩阵方便后续使用,代码如下

def loadData():
    data=np.array([[3,3],[4,3],[1,1]]) #导入数据
    labels=np.array([1,1,-1])          #导入标签
    gram=np.ones((len(labels),len(labels)))
    for i in range(len(labels)):
        for j in range(len(labels)):
            gram[i][j]=int(np.dot(data[i,:],data[j,:]))  #求各个实例之间的内积,存储在gram矩阵中
    return data,labels,gram

       此时打印data与gram矩阵可得

data=[[3 3]
      [4 3]
      [1 1]]


gram=[[18. 21.  6.]
      [21. 25.  7.]
      [ 6.  7.  2.]]

 2.3 初始化

       与感知机原始形式不同,初始化中权重w在对偶形式中被替换为\alpha_i,设学习率\eta=1,可得初始化代码如下 

class PLA:
    def __init__(self,data,labels,gram,a=1): #初始化
        self.data=data                  #实例的数据
        self.labels=labels              #实例的标签
        self.gram=gram                  #提前计算样本的gram矩阵
        self.a=a                        #a为学习率
        self.alpha=np.zeros((data.shape[0],1)) #初始权重为0
        self.bias=0;                      #bias为偏置
        self.numdata=data.shape[0]         #样本数
        self.numfeatures=data.shape[1]    #特征数

2.4写sign函数

       由于误分类点的判断条件写在了后文模型训练的部分,故此处的sign函数就为最基础的符号函数,代码如下

    def sign(self,flag):
        if flag>0:
            return 1
        else:
            return -1

2.5更新参数

     参照上文步骤(3)所示的更新参数 ,代码如下

    def update(self,i,labels_i):
        self.alpha[i,:]+=1  #alpha=alpha+eta(学习率)
        self.bias+=labels_i #b=b+eta*yi

    由于此案例中学习率\eta=1,故更新\alpha_i时直接加1了。

2.6模型训练

    将样本点代入模型进行迭代运算,直至不再出现误分类点,即感知机模型训练完成。对偶形式相对于原始形式,其误分类点的判断形式计算量较大,可以分步计算括号内的各项,大不了多设几个变量,代码如下

    def trainPLA(self):
        bMisClassify=True
        while bMisClassify:
            mMisClassifyNum=0
            for i in range(self.numdata):
                flag=0
                for j in range(self.numdata):
                    flag=flag+self.alpha[j,:]*self.labels[j]*self.gram[j][i]
                flag=self.labels[i]*(flag+self.bias) #计算判别条件
                if self.sign(flag)==-1:
                    mMisClassifyNum+=1
                    self.update(i,self.labels[i])
            if mMisClassifyNum==0:
                bMisClassify=False
        print('The PLA Training has finished! The alpha is :\n',self.alpha)
        return self.alpha,self.bias

    返回值为\alpha_i以及偏置b,计算过程中可以直接调用在数据初始化过程中已经计算过的Gram矩阵。

2.7可视化

    可视化中需要将\alpha_i转换成w,其转换公式为w=\sum_{i=1}^N{\alpha _iy_ix_i},要注意的是其中涉及的矩阵运算较多,需要保证各个矩阵的维度满足运算要求,当不满足要求时需要将矩阵进行类似转置之类的操作,代码如下。

class Plot:
    def __init__(self,x,y,alpha,bias):
        self.alpha=alpha
        self.bias=bias
        tmp=np.zeros((1,x.shape[1]))
        w=np.zeros((x.shape[1],1))
        plt.figure(1)
        plt.title('Perceptron Learning Algorithm')
        plt.xlabel('X0')
        plt.ylabel('X1')

        xdata=np.arange(0,7)                  #x轴范围
        for i in range(x.shape[0]):
            tmp+=self.alpha[i][0]*y[i]*x[i,:]
        w=tmp.T                              #对tmp进行转置操作
        ydata=-(self.bias+w[0][0]*xdata)/w[1][0]

        plt.plot(xdata,ydata,c='r')

        for i in range(len(y)):
            if y[i]==1:
                plt.scatter(x[i,0],x[i,1],c='g',marker='o')
            else:
                plt.scatter(x[i,0],x[i,1],c='b',marker='x')
        plt.savefig('Blog1.png')
        plt.show()

2.8整体代码

    添加主程序后,整体代码如下

import numpy as np
import matplotlib.pyplot as plt

def loadData():
    data=np.array([[3,3],[4,3],[1,1]]) #导入数据
    labels=np.array([1,1,-1])          #导入标签
    gram=np.ones((len(labels),len(labels)))
    for i in range(len(labels)):
        for j in range(len(labels)):
            gram[i][j]=int(np.dot(data[i,:],data[j,:]))  #求各个实例之间的内积,存储在gram矩阵中
    return data,labels,gram

class PLA:
    def __init__(self,data,labels,gram,a=1): #初始化
        self.data=data                  #实例的数据
        self.labels=labels              #实例的标签
        self.gram=gram                  #提前计算样本的gram矩阵
        self.a=a                        #a为学习率
        self.alpha=np.zeros((data.shape[0],1)) #初始权重为0
        self.bias=0;                      #bias为偏置
        self.numdata=data.shape[0]         #样本数
        self.numfeatures=data.shape[1]    #特征数

    def sign(self,flag):
        if flag>0:
            return 1
        else:
            return -1

    def update(self,i,labels_i):
        self.alpha[i,:]+=1  #alpha=alpha+eta(学习率)
        self.bias+=labels_i #b=b+eta*yi

    def trainPLA(self):
        bMisClassify=True
        while bMisClassify:
            mMisClassifyNum=0
            for i in range(self.numdata):
                flag=0
                for j in range(self.numdata):
                    flag=flag+self.alpha[j,:]*self.labels[j]*self.gram[j][i]
                flag=self.labels[i]*(flag+self.bias) #计算判别条件
                if self.sign(flag)==-1:
                    mMisClassifyNum+=1
                    self.update(i,self.labels[i])
            if mMisClassifyNum==0:
                bMisClassify=False
        print('The PLA Training has finished! The alpha is :\n',self.alpha)
        return self.alpha,self.bias

class Plot:
    def __init__(self,x,y,alpha,bias):
        self.alpha=alpha
        self.bias=bias
        tmp=np.zeros((1,x.shape[1]))
        w=np.zeros((x.shape[1],1))
        plt.figure(1)
        plt.title('Perceptron Learning Algorithm')
        plt.xlabel('X0')
        plt.ylabel('X1')

        xdata=np.arange(0,7)                  #x轴范围
        for i in range(x.shape[0]):
            tmp+=self.alpha[i][0]*y[i]*x[i,:]
        w=tmp.T                              #对tmp进行转置操作
        ydata=-(self.bias+w[0][0]*xdata)/w[1][0]

        plt.plot(xdata,ydata,c='r')

        for i in range(len(y)):
            if y[i]==1:
                plt.scatter(x[i,0],x[i,1],c='g',marker='o')
            else:
                plt.scatter(x[i,0],x[i,1],c='b',marker='x')
        plt.savefig('Blog1.png')
        plt.show()

if __name__=='__main__':
    data,labels,gram=loadData()
    print(data)
    print(gram)
    myPLA=PLA(data,labels,gram)
    alpha,bias=myPLA.trainPLA()
    Plot(data,labels,alpha,bias)

     运行后输出\alpha_i和权重矩阵w的结果为

The PLA Training has finished! The alpha is :
 [[2.]
 [0.]
 [5.]]


w=[[1.]
   [1.]]

      绘制出的分离超平面如下图所示

       与笔记1中得到的分离超平面一致,完全将正负实例点分离,具体迭代过程见书中下表

3学习心得

       感知机的对偶形式相较于感知机的原始形式更具有一般性,两者的大致思路相近,故在掌握感知机学习算法的原始形式之后,仅需在原始形式代码的基础上稍加修改即可。本章学习了感知机模型,它是根据输入实例的特征向量x对其进行二分类的线性分类模型,将学习的过程转化为使用梯度下降法不断极小化损失函数 。下一章就要学习k近邻法了,继续加油!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值