1. 感知机模型
感知机(Perceptron)是一个线性分类器,其只适用于线性可分的数据。其数学表达形式为:
$$f(\mathbf{x}) = \text{sign}(\mathbf{w}^\mathrm{T} \mathbf{x} + b)$$
其中,$\mathbf{w}$ 是权重向量,$b$ 是偏置,$\mathbf{x}$ 是输入特征。函数 $\text{sign}$ 将输出映射到 -1 或 1。
感知机的目标是在所有的线性可分超平面构成的假设空间中,找到一个能使训练集中的数据可分的超平面。感知机并未对这一超平面做特殊要求,只要能区分开训练数据即可。因此,它找到的超平面并不一定是最优的,即可能只是恰好拟合了训练数据的超平面,其泛化能力并不佳。
2. 学习策略
由于直接最小化误分类点的个数并不可微,感知机的学习策略被设为:最小化误分类点到超平面的距离。
假设超平面为 $\mathbf{w}^\mathrm{T}\mathbf{x} + b = 0$,样本点 $(\mathbf{x}_i, y_i)$ 到超平面的距离为:
$$\frac{1}{||\mathbf{w}||} |\mathbf{w}^\mathrm{T}\mathbf{x}_i + b|$$
对于误分类点,有 $y_i(\mathbf{w}^\mathrm{T}\mathbf{x}_i + b) < 0$,因此损失函数可以定义为:
$$L(\mathbf{w}, b) = -\sum_{i \in M} y_i(\mathbf{w}^\mathrm{T}\mathbf{x}_i + b)$$
其中 $M$ 为误分类点的集合。
在初始化超平面的参数即法向量和偏置后,迭代地计算样本的损失并求出参数的梯度,再基于随机梯度下降(SGD)来更新参数即可,直到损失收敛后停止学习。
3. 基于 Numpy 的感知机实现
以下代码展示了如何使用 Python 和 Numpy 从零实现感知机算法。
import numpy as np
def prepare_data(n=100):
"""
生成 OR 门数据集
"""
def OR(x):
# 定义 OR 门的真实权重和偏置用于生成标签
w_true = np.array([0.5, 0.5])
b_true = -0.2
tmp = np.sum(w_true * x) + b_true
# 统一使用 -1 和 1 作为标签以匹配感知机更新规则
if tmp <= 0:
return -1
else:
return 1
input_size = 2
inputs = np.random.randn(n, input_size)
labels = np.array([OR(inputs[i]) for i in (n)])
inputs, labels
:
():
.w = np.random.randn(input_size)
.b = np.random.randn()
.lr = lr
():
tmp = np.(.w * x) + .b
tmp <= :
-
:
():
.w = .w + .lr * y * x
.b = .b + .lr * y
__name__ == :
n =
ratio =
input_size =
(.(n))
X, Y = prepare_data(n)
clip_num = (n * ratio)
train_X, train_Y = X[:clip_num], Y[:clip_num]
test_X, test_Y = X[clip_num:], Y[clip_num:]
lr =
model = Perceptron(input_size, lr)
s = model.predict(X[])
(.(X[][], X[][], s))
epochs =
i (epochs):
loss =
wrong_index = []
(.(i+))
()
idx (clip_num):
pred_y = model.predict(train_X[idx])
pred_y != train_Y[idx]:
wrong_index.append(idx)
tmp_loss = ((np.(model.w * train_X[idx]) + model.b))
loss += tmp_loss
(.((wrong_index), loss))
()
j wrong_index:
model.update(train_X[j], train_Y[j])
wrong_num =
test_loss =
j (test_X.shape[]):
pred_y = model.predict(test_X[j])
pred_y != test_Y[j]:
tmp_loss = ((np.(model.w * test_X[j]) + model.b))
test_loss += tmp_loss
wrong_num +=
(.(wrong_num, test_loss))


