![b854dd5f2492aeff7d16884fa2642f35.png](https://i-blog.csdnimg.cn/blog_migrate/8405ecd4d31532892f60f77b388aacb3.png)
问题
为了可视化方便,我们假设数据是二维的,如图1所示。
![cd37320abe14dee140ac65630099927e.png](https://i-blog.csdnimg.cn/blog_migrate/0071ddc610c0d84ad79ea3d373318ae5.jpeg)
蓝色圆点和红色叉号分别代表不同的两类,这堆数据数据可以用一条黑色直线把这两类完美分开,那么这堆数据就是线性可分的。
线性不可分数据如图2
![2ade49f1e6f9ac9ebb5ddeb7b919d135.png](https://i-blog.csdnimg.cn/blog_migrate/583ad63e72043d6e81ceb48f2df9d8a1.jpeg)
那么我们的问题就是,如何从线性可分数据中学习出一个比较好的模型用于分类。
模型
设有数据集
其中
感知机的几何解释就是学出一个
损失函数
用误分类率做损失函数是很自然的一个想法,但是误分类点数这个东西不可导,我们需要找一些光滑可导性质的损失函数便于利用梯度进行优化。因此选用所有误分类点到超平面的距离作为损失函数,如下
先来解释上面公式,
合并起来就是对于所有误分类点
虽然
最后我们记
优化
梯度下降法求解,分别求其梯度
这里用随机梯度下降法 (SGD),每次选取一个误分类点进行更新模型,更新公式如下
其中
接下来我们都假设
![a7e9199566e04499674b3487f3544d99.png](https://i-blog.csdnimg.cn/blog_migrate/146865ce58f1ee112c68f49988516d7f.jpeg)
- 当
,对于误分类点
,意味着
为了让
2. 当
合并1.2.,我们得到
收敛性
设数据集线性可分,则存在一个超平面把数据集分开,记此时的参数为
看起来更新后的
由于感知机模型是只在分类错误时才更新,因此必有
因为
上面说明了每次更新,模型是向最终结果不断靠近的,接下来给出模型更新次数的一个上界,也就是最多要更新多少次。
记
假设最大更新次数为
对偶形式
因为更新公式为
那么对偶形式相较于原始形式有什么优点呢?原始的形式只要没收敛,就需要对数据集中每个数据点都计算
使用对偶形式后,我们可以预先计算所有数据点之间的内积,构成一个
代码实现
原始形式
import numpy as np
# 准备数据
# 数据格式(x1, x2, y)
T = np.array([
[3, 3, 1],
[4, 3, 1],
[1, 1, -1],
])
# 初始化
w = np.zeros((1, 2))
b = 0
# 学习算法
i = 0
while i < T.shape[0]:
x = T[i, :2].reshape((-1, 1))
y = T[i, -1]
if y * (w @ x + b) <= 0:
# 误分类
# 求梯度
grad_w = -y * x.reshape(1, -1)
grad_b = -y
# 梯度下降更新
w -= grad_w
b -= grad_b
print('[Error: x{}][new_w:{}][new_b:{}]'.format(i, w, b))
# 出错了,从头开始验证数据集
i = 0
else:
i += 1
对偶形式
import numpy as np
# 准备数据
# 数据格式(x1, x2, y)
T = np.array([
[3, 3, 1],
[4, 3, 1],
[1, 1, -1],
])
# 初始化
a = np.zeros(T.shape[0])
b = 0
lr = 1
# 求Gram Matrix
gram_matrix = T[:, :-1] @ T[:, :-1].T
# 学习算法
i = 0
while i < T.shape[0]:
if T[i, -1] * ((a * T[:, -1] * gram_matrix[i, :]).sum() + b) <= 0:
# 误分类
# 更新
a[i] += 1
b += T[i, -1]
i = 0
print(a, b)
else:
i += 1
参考资料
- 机器学习基石. 林轩田, 国立台湾大学.
- 统计学习方法(第2版). 李航.