Python手撸机器学习系列(一):感知机 (附原始形式和对偶形式Python实现代码)

感知机

1.感知机的定义

感知机是二分类的线性模型,是神经网络和SVM的基础。输入特征x∈Xx∈XxX,输出y={+1,−1}y = \{+1 , -1\}y={+1,1}

那么感知机算法可以表示为f(x)=sign(w⋅x+b)f(x) = sign(w·x+b)f(x)=sign(wx+b),相当于一个简单的线性函数

其中sign(a)={+1,if a≥0−1,if a<0 sign(a)= \begin{cases} +1, & \text {if a$\geq$0} \\ -1, & \text{if a<0} \end{cases} sign(a)={+1,1,if a0if a<0

数据的线性可分性:存在w⋅x+b=0w·x+b = 0wx+b=0能将数据集中的正负样本分开。说人话就是能找到一条直线将两组不同的点分开。

在这里,感知机的数据集假设为线性可分的,即表示在一堆坐标点中,总能找到一条线将正副样本给分开,并且一般能找到多条线满足要求,如下图所示。

请添加图片描述

2.感知机原始形式

2.1 损失函数

损失函数若选取误分类点的个数,则对于wwwbbb而言不连续可导,不易优化

所以,选取误分类点到超平面S的总距离作为损失函数,即−1∣∣w∣∣∑xi∈Myi(w⋅xi+b)-\frac{1}{||w||}\displaystyle\sum_{x_i∈M}y_i(w·x_i+b)w1xiMyi(wxi+b),最终不考虑1∣∣w∣∣\frac{1}{||w||}w1,即得到最终的损失函数:
L(w,b)=−∑xi∈Myi(w⋅xi+b) L(w,b) = -\displaystyle\sum_{x_i∈M}y_i(w·x_i+b) L(w,b)=xiMyi(wxi+b)
推导:某一点到S的距离为−1∣∣w∣∣∣w⋅x0+b∣-\frac{1}{||w||}|w·x_0+b|w1wx0+b,而误分类数据会有−yi(w⋅xi+b)>0-y_i(w·x_i+b)>0yi(wxi+b)>0,所以上上式可以转化为−1∣∣w∣∣yi(w⋅xi+b)-\frac{1}{||w||}y_i(w·x_i+b)w1yi(wxi+b),总距离:−1∣∣w∣∣∑xi∈Myi(w⋅xi+b)-\frac{1}{||w||}\displaystyle\sum_{x_i∈M}y_i(w·x_i+b)w1xiMyi(wxi+b)

L(w,b)L(w,b)L(w,b)非负,没有误分类点则为0

2.2 计算过程

使用随机梯度下降(SGD)来优化参数,算法如下:

  1. 选取初值w0w_0w0b0b_0b0

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

  3. 如果yi(w⋅xi+b)≤0y_i(w·x_i+b)\leq 0yi(wxi+b)0,则有:

    w=w+ηyixiw = w+\eta y_ix_iw=w+ηyixi

    b=b+ηyib = b + \eta y_ib=b+ηyi

  4. 转至2,直到没有误分类点

其中η\etaη为学习率,wwwbbb的梯度通过对损失函数L(w,b)L(w,b)L(w,b)求导而来

2.3代码实现

import numpy as np
import matplotlib.pyplot as plt

x_true = np.array([[3,3],[4,3]])
x_false = np.array([[1,1]])
y = [1]* len(x_true) + [-1] * len(x_false)
x_all = np.vstack([x_true,x_false])

w = np.array([0,0])
lr = 1
b = 0
i = 0
#循环判断每一个样本有没有误分类,有则更新参数重新开始判断
while i<len(x_all):
    if y[i]*(w.dot(x_all[i].T)+b) <= 0:
        w = w + lr * y[i] * x_all[i]
        b = b + lr * y[i]
        i = 0
        print('w = {},b = {}'.format(w,b))
    else:
        i += 1
print('平面S为:{:.2f}x1 + {:.2f}x2 {} = 0'.format(w[0],w[1], str(b) if b < 0 else '+'+str(b)))
plot_x = [0,1,2,3,4,5]
plot_y = [-(x*w[0]+b)/w[1] for x in plot_x]
plt.figure(figsize =(10,10))
plt.scatter([x[0] for x in x_true], [x[1] for x in x_true] , c = 'blue')
plt.scatter([x[0] for x in x_false], [x[1] for x in x_false] , c = 'red')
plt.plot(plot_x , plot_y , c = 'black')
# plt.text(0.5,4.5,'Func:{:.2f}x1 + {:.2f}x2 {} = 0'.format(w[0],w[1], str(b) if b < 0 else '+'+str(b)),fontsize=15,color = "green",style = "italic")
plt.xlim(0, 5.0) #坐标轴
plt.ylim(0, 5.0)
plt.xlabel('x1',fontsize = 16)
plt.ylabel('x2',fontsize = 16)
plt.pause(0.001)
plt.show()

实现结果:

请添加图片描述

wwwbbb变化过程以及最终的平面S:

请添加图片描述

换一组更复杂的数据测试:
请添加图片描述

3.感知机对偶形式

对偶形式是将原始形式中的wwwbbb表示为xix_ixiyiy_iyi的线性组合,即

{w=∑i=1Nniyixib=∑i=1Nniyi\begin{cases} w =\displaystyle\sum_{i = 1}^Nn_i y_ix_i \\b = \displaystyle\sum_{i=1}^Nn_i y_i \end{cases} w=i=1Nniyixib=i=1Nniyi

nin_ini值越大,表示这个样本被误分类的次数越多,就意味着这个点离我们所需要的超平面越近,左移一点或者右移一点就会误分类,对于SVM而言,这个点极有可能就是支持向量

根据原始形式,f(x)=sign(wx+b)=sign(∑j=1Nnjyjxj⋅x+∑i=1Nniyj)f(x) = sign(wx+b) = sign(\displaystyle\sum_{j=1}^Nn_j y_jx_j·x+\displaystyle\sum_{i=1}^Nn_i y_j)f(x)=sign(wx+b)=sign(j=1Nnjyjxjx+i=1Nniyj)

从之前的的优化wwwbbb,变成了优化nnn

误分类的判断条件也变成了yi(∑j=1Nnjyjxj⋅x+∑i=1Nniyj)<0y_i(\displaystyle\sum_{j=1}^Nn_j y_jx_j·x+\displaystyle\sum_{i=1}^Nn_i y_j)<0yi(j=1Nnjyjxjx+i=1Nniyj)<0

3.1 计算过程

《统计学习方法》中将nin_iniαi\alpha_iαi表示

  1. 选取初值α\alphaαbbb

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

  3. 如果yi(∑j=1Nαjyjxj⋅xi+b)≤0y_i(\displaystyle\sum_{j=1}^N\alpha_jy_jx_j·x_i+b)\leq 0yi(j=1Nαjyjxjxi+b)0,则有:

    α=α+η\alpha = \alpha+\etaα=α+η

    b=b+ηyib = b + \eta y_ib=b+ηyi

  4. 转至2,直到没有误分类点

在对偶形式中,样本以内积的形式计算,如果以内积矩阵形式存储,则会大大缩短计算时间,即Gram矩阵:

G=[xi⋅xj]G = [x_i·x_j]G=[xixj],代码可以表示成Gram = x.dot(x.T)

3.2 代码实现

import numpy as np
import matplotlib.pyplot as plt

x_true = np.array([[3, 3], [4, 3]])
x_false = np.array([[1, 1]])
x_all = np.vstack([x_true,x_false])
y = [1]*len(x_true) + [-1] * len(x_false)
n = len(x_all)


a = np.zeros(n)
b = 0
lr = 1

Gram = x_all.dot(x_all.T) #计算G

i = 0
#循环判断每一个样本有没有误分类,有则更新参数重新开始判断
while i < n:
    error = 0
    for j in range(n):
        error += a[j] * y[j] * Gram[j,i]
    if y[i] * (error + b) <= 0: #有负样本
        a[i] += lr
        b += lr * y[i]
        print('a = {},b = {}'.format(a,b))
        i = 0
    else:
        i += 1

w = np.zeros(2)
for j in range(n):
    w += a[j] * y[j] * x_all[j]

print('平面S为:{:.2f}x1 + {:.2f}x2 {} = 0'.format(w[0],w[1], str(b) if b < 0 else '+'+str(b)))

plot_x = [0,1,2,3,4,5]
plot_y = [-(x*w[0]+b)/w[1] for x in plot_x]
plt.figure(figsize =(10,10))
plt.scatter([x[0] for x in x_true], [x[1] for x in x_true] , c = 'blue')
plt.scatter([x[0] for x in x_false], [x[1] for x in x_false] , c = 'red')
plt.plot(plot_x , plot_y , c = 'black')
plt.xlim(0, 5.0) #坐标轴
plt.ylim(0, 5.0)
plt.xlabel('x1',fontsize = 16)
plt.ylabel('x2',fontsize = 16)
plt.pause(0.001)
plt.show()

实验结果:

请添加图片描述

其中aaabbb的变化过程,以及最终的平面S:

在这里插入图片描述

4.结语

本为初学者,难免有错误,有问题欢迎评论区指出或私信。

联系方式:1759412770@qq.com ; zn1759412770@163.com

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

锌a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值