感知机原理python实现

感知机二类分类的线性分类模型,输入为实例的特征向量,输出为实例的类别,取+1和-1二值,感知机对应于输入空间中将实例划分为正负两类的分离超平面,属于判别模型。
感知机学习旨在求出将训练数据进行线性划分的分离超平面,为此导入基于误分类的损失函数,利用梯度下降法对损失函数进行极小化,求得感知机模型。感知机学习算法具有简单、易于实现的优点,感知机预测是用学习到的感知机模型对新的输入实例进行分类。

import matplotlib.pyplot as plt
import numpy as np
import random

加载数据和可视化数据

"""
函数说明:读取数据

Parameters:
    fileName - 文件名
Returns:
    dataMat - 数据矩阵
    labelMat - 数据标签
"""
def loadDataSet(fileName):
    dataMat=[]
    labelMat=[]
    fr=open(fileName)
    for line in fr.readlines():
        lineArr=line.strip().split('\t')
        dataMat.append([float(lineArr[0]),float(lineArr[1])])#添加数据
        labelMat.append(float(lineArr[2]))
    return dataMat,labelMat
    
"""
函数说明:数据可视化

Parameters:
    dataMat - 数据矩阵
    labelMat - 数据标签
Returns:"""
def showDataSet(dataMat,labelMat):
    data_plus=[]
    data_minus=[]
    for i in range(len(dataMat)):
        if labelMat[i]>0:
            data_plus.append(dataMat[i])
        else:
            data_minus.append(dataMat[i])
    data_plus_np=np.array(data_plus)
    data_minus_np=np.array(data_minus)
    plt.scatter(np.transpose(data_plus_np)[0],np.transpose(data_plus_np)[1])
    plt.scatter(np.transpose(data_minus_np)[0],np.transpose(data_minus_np)[1])
    plt.show()

dataMat,labelMat=loadDataSet("testSet.txt")
showDataSet(dataMat,labelMat)
'''可以看到数据集具有线性可分性:即数据集T={(x1,y1),(x2,y2),…,(xn,yn)},其中xi∈X,yi∈Y={+1,-1},存在某个超平面S,w•x+b=0能够将数据集的正实例点和负实例点完全正确地划分到超平面两侧'''

1.感知机模型

输入:实例特征向量 x ∈ R n x\in R^n xRn
输出:实例类别 y ∈ + 1 , − 1 y\in {+1,-1} y+1,1
映射: y = s i g n ( w x + b ) y=sign(wx+b) y=sign(wx+b)

2.感知机策略

损失函数的一个选择是误分类点的总数,但是这样的损失函数不是参数w,b的连续可导函数,不易优化,另一个选择是误分类点到超平面S的总距离,这是感知机所采用的。损失函数定义为
$L(w,b)=-\sum_{x\in M}^{}y_i(wx_i+b) $
其中,M为误分类点的集合。损失函数是非负的,如果没有误分类点,损失函数值为0,而且误分类点数越少,误分类点离超平面越近,损失函数值越小。

3.感知机算法

感知机算法是误分类驱动的,具体所采用的是随机梯度下降法,首先任意选取一个超平面w0,b0,然后用梯度下降法不断地极小化目标函数,极小化的过程中不是一次使M中所有误分类点的梯度下降,而是一次随机选取一个误分类点使其梯度下降。

使用梯度下降求解下面最优化问题
$\min_{w,b}L(w,b)=-\sum_{x_i\in M}^{}y_i(wx_i+b) $
选取误分类点进行更新:
▽ w = ∑ x i ∈ M y i x i \bigtriangledown _w=\sum_{x_i\in M}^{}y_ix_i w=xiMyixi
▽ b = ∑ x i ∈ M y i \bigtriangledown _b=\sum_{x_i\in M}^{}y_i b=xiMyi
w = w − η y i x i w=w-\eta y_ix_i w=wηyixi
b = b − η y i b=b-\eta y_i b=bηyi

class perceptron(object):
    #感知机的参数
    def __init__(self,W,b):
        self.W=W
        self.b=b

    #预测输入实例的类别
    def predict(self,X):
        y=X@self.W+b
        y=np.sign(y)
        return y
    
    #计算损失函数和梯度
    def loss(self,X,y):
        loss=-y*(X@self.W.T+self.b)#
        dW=-y*X
        db=-y
        return loss,dW,db
    
    #训练感知机
    def train(self,X,y,learning_rate=1e-2,epoch=100):
        loss_history=[]
        w_history=[]
        b_hsitory=[]
        for it in range(epoch):
            num_train=X.shape[0]
            for i in range(num_train):
                y_pred=np.sign(X[i]@self.W.T+self.b)
                if y_pred*y[i]<=0:  #判断是否是误分类点
                    loss,dW,db=self.loss(X[i],y[i])#计算损失和梯度
                    loss_history.append(loss)
                    self.W=self.W-learning_rate*dW#更新参数
                    self.b=self.b-learning_rate*db
                    w_history.append(self.W)
                    b_hsitory.append(self.b)
        return loss_history,w_history,b_hsitory

num_train=len(dataMat)
#初始化参数
W=np.random.randn(1,2)
b=1
#定义模型
model=perceptron(W,b)
#使用全部数据集进行训练的次数
epoch=200
#学习率
learning_rate=1e-2
#训练感知机模型
loss_history,w_history,b_hsitory=model.train(np.array(dataMat),np.array(labelMat),learning_rate,epoch)
print("更新次数",len(loss_history))
#输出损失函数的变化曲线
plt.scatter(np.transpose(list(range(len(loss_history)))),loss_history)
plt.show()
'''可以看到损失函数逐渐趋向于0,说明训练成功'''
'''由于使用随机梯度下降法,所以损失函数变化比较剧烈'''

#查看训练好的参数
print(model.W)
print(model.b)
"""
函数说明:分类结果可视化

Parameters:
	dataMat - 数据矩阵
    w - 直线法向量
    b - 直线解决
Returns:
    无
"""
def showClassifer(dataMat, w, b):
	#绘制样本点
	data_plus = []                                  #正样本
	data_minus = []                                 #负样本
	for i in range(len(dataMat)):
		if labelMat[i] > 0:
			data_plus.append(dataMat[i])
		else:
			data_minus.append(dataMat[i])
	data_plus_np = np.array(data_plus)              #转换为numpy矩阵
	data_minus_np = np.array(data_minus)            #转换为numpy矩阵
	plt.scatter(np.transpose(data_plus_np)[0], np.transpose(data_plus_np)[1], s=30, alpha=0.7)   #正样本散点图
	plt.scatter(np.transpose(data_minus_np)[0], np.transpose(data_minus_np)[1], s=30, alpha=0.7) #负样本散点图
	#绘制直线
	x1 = max(dataMat)[0]
	x2 = min(dataMat)[0]
	a1, a2 = w
	b = float(b)
	a1 = float(a1)
	a2 = float(a2)
	y1, y2 = (-b-a1*x1)/a2, (-b - a1*x2)/a2
	plt.plot([x1, x2], [y1, y2])
	plt.show()


showClassifer(dataMat,model.W[0],model.b)
'''可以看到决策平面正确的分开了两个类别'''
'''感知机的几何解释:线性方程w•x+b=0对应于特征空间R中的一个超平面S(分离超平面),其中w是超平面的法向量,b是超平面的截距,这个超平面将特征空间划分为两个部分,位于两部分的点被分为正负两类。'''
#查看迭代过程中分类平面的变化
print("更新次数",len(w_history))
for i in  range(3,len(w_history),30):
    print(i)
    showClassifer(dataMat,w_history[i][0],b_hsitory[i])
'''直观解释——当一个实例点被误分类,位于分离超平面的错误一侧时,则调整w,b的值,使分离超平面向该误分类点的一侧移动,以减少该误分类点与超平面间的距离,直至超平面越过该误分类点使其被正确分类。'''

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值