感知机
感知机(perceptron)是支持向量机的和神经网络的基础。优点是简单易实现,缺点是只能分离线性可分数据集, 否则不收敛,且感知机学习得到的分离超平面不是唯一解,会因为初始值的选择以及选择训练样本的次序而改变。
核心思想
建立分离超平面,将正负实例点划分到超平面的两侧
模型
输入到输出的函数:
f(x)=sign(wx+b)
f
(
x
)
=
s
i
g
n
(
w
x
+
b
)
策略
损失函数最小化。很自然的想法就是误分类样本个数作为损失函数。但这对于参数w, b而言不易优化(不连续可导),故采用误分类样本点到分离超平面距离之和最小化为策略。没有误分类点时距离之和为0:
−1||w||∑xi∈Myi(wxi+b)
−
1
|
|
w
|
|
∑
x
i
∈
M
y
i
(
w
x
i
+
b
)
其中M是误分类样本点集合,不考虑
−1||w||
−
1
|
|
w
|
|
得到损失函数:
−∑xi∈Myi(wxi+b)
−
∑
x
i
∈
M
y
i
(
w
x
i
+
b
)
学习算法
随机梯度下降算法
算法流程
- Input: 训练数据集,学习率eta
- Output: 权值w, 阈值b
- Step1: 初始化权值w, b
- Step2: 随机梯度下降算法更新w, b,针对每个样本进行训练直到所有样本均分类正确
对偶形式感知机
模型
当初始化权值w为0向量。梯度下降更新权值 w←w+ηyixi w ← w + η y i x i ,若每个样本学习了 αi α i 次,那么:
w=∑i=1Nηαiyixi
w
=
∑
i
=
1
N
η
α
i
y
i
x
i
其中N为样本总数。
αi
α
i
可以视为每个样本的权值,训练次数越多则表明其距离分离超平面越远。阈值b同理。
将感知机中w的训练改为对 αi α i 的训练即可训练对偶形式的感知机。
代码
"""
感知机(perceptron):原始形式以及对偶形式
"""
import numpy as np
class Perceptron:
def __init__(self, eta=1):
self.eta = eta # 学习率
self.w = None # 权值
self.b = None # 阈值
def fit(self, X_data, y_data):
self.w = np.zeros(X_data.shape[1]) # 初始化
self.b = 0
change = True
while change: # w, b 不发生改变则结束训练
for X, y in zip(X_data, y_data): # 依次输入每个数据点进行训练
change = False
while y * (self.w @ X + self.b) <= 0:
self.w += self.eta * X * y
self.b += self.eta * y
change = True
return
def predict(self, X):
return np.sign(self.w @ X + self.b)
class Perceptron_dual:
# 对偶形式的感知机
def __init__(self, eta=1):
self.eta = eta
self.alpha = None # alpha相当于样本的权值,当eta为1时就是每个样本参与训练的次数
self.b = None
self.N = None
self.gram = None
def init_param(self, X_data):
self.N = X_data.shape[0]
self.alpha = np.zeros(self.N)
self.b = 0
self.gram = self.getGram(X_data)
def getGram(self, X_data):
# 计算Gram矩阵
gram = np.diag(np.linalg.norm(X_data, axis=1) ** 2)
for i in range(self.N):
for j in range(i + 1, self.N):
gram[i, j] = X_data[i] @ X_data[j]
gram[j, i] = gram[i, j]
return gram
def sum_dual(self, y_data, i):
s = 0
for j in range(self.N):
s += self.alpha[j] * y_data[j] * self.gram[j][i]
return y_data[i] * (s + self.b)
def fit(self, X_data, y_data):
self.init_param(X_data)
changed = True
while changed:
changed = False
for i in range(self.N): # 依次输入每个数据点进行训练
while self.sum_dual(y_data, i) <= 0:
self.alpha[i] += self.eta
self.b += self.eta * y_data[i]
changed = True
return
if __name__ == '__main__':
X_data = np.array([[3, 3], [4, 3], [1, 1]])
y_data = np.array([1, 1, -1])
p = Perceptron()
p.fit(X_data, y_data)
print(p.w, p.b)
我的GitHub
注:如有不当之处,请指正。