感知机学习算法是对以下最优化问题的算法。给定一个训练数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
.
.
.
,
(
x
N
,
y
N
)
}
T = \{(x_1,y_1), (x_2,y_2),...,(x_N,y_N)\}
T={(x1,y1),(x2,y2),...,(xN,yN)}其中,
x
i
∈
X
=
R
n
,
y
i
∈
Y
=
{
+
1
,
−
1
}
,
i
=
1
,
2
,
⋯
,
N
x_{i} \in \mathcal{X}=\mathbf{R}^{n}, \quad y_{i} \in \mathcal{Y}=\{+1,-1\}, \quad i=1,2, \cdots, N
xi∈X=Rn,yi∈Y={+1,−1},i=1,2,⋯,N,求参数
w
w
w,
b
b
b,使其转化为以下损失函数极小化问题的解:
minL
(
w
,
b
)
=
−
∑
x
i
∈
M
y
i
(
w
∗
x
i
+
b
)
\operatorname{minL}(w, b)=-\sum_{x_i \in M}y_i(w * x_i+b)
minL(w,b)=−xi∈M∑yi(w∗xi+b)其中M为误分类点的集合。
感知机学习算法采用随机梯度下降法(不同于批量梯度下降法,每次随机选择一个误分类点使其梯度下降)
损失函数
L
(
w
,
b
)
L(w,b)
L(w,b)的梯度定义为:
∇
w
L
(
w
,
b
)
=
−
∑
x
i
∈
M
y
i
x
i
∇
b
L
(
w
,
b
)
=
−
∑
x
i
∈
M
y
i
1
\begin{aligned} \nabla_{w} L(w, b) &=-\sum_{x_{i} \in M} y_{i} x_{i} \\ \nabla_{b} L(w, b) &=-\sum_{x_{i} \in M} y_{i} \end{aligned} \qquad 1
∇wL(w,b)∇bL(w,b)=−xi∈M∑yixi=−xi∈M∑yi1首先初始化
w
0
w_0
w0、
b
0
b_0
b0(可任意选取),然后随机的选择一个误分类点
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi),对
w
,
b
w,b
w,b进行更新:
w
←
w
+
η
y
i
x
i
b
←
b
+
η
y
i
2
\begin{aligned}w \leftarrow w+\eta y_{i} x_{i} \\ b \leftarrow b+\eta y_{i} \end{aligned} \qquad 2
w←w+ηyixib←b+ηyi2其中
η
(
0
≤
η
≤
1
)
\eta(0\le \eta \le1)
η(0≤η≤1)表示步长,即学习率。
*注:“梯度反映的是空间变量变化趋势的最大值和方向”,梯度下降算法时的原理就是利用了梯度的定义,梯度向量从几何意义上讲,就是函数变化增加最快的地方,沿着梯度向量的方向更容易找到函数的最大值,沿着向量相反的方向,梯度减小最快,更容易找到函数最小值。因此选用了梯度的反方向去更新参数 w , b w,b w,b以对损失函数进行迭代,由此可以从 1 1 1式得到 2 2 2式,符号的变化是因为方向相反。
感知机算法原始形式
输入:线性可分的训练数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
…
,
(
x
N
,
y
N
)
}
T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \ldots,\left(x_{N}, y_{N}\right)\right\}
T={(x1,y1),(x2,y2),…,(xN,yN)},其中
x
i
∈
X
=
R
n
,
y
i
∈
Y
=
{
+
1
,
−
1
}
,
i
=
1
,
2
,
⋯
,
N
x_{i} \in \mathcal{X}=\mathbf{R}^{n}, \quad y_{i} \in \mathcal{Y}=\{+1,-1\}, \quad i=1,2, \cdots, N
xi∈X=Rn,yi∈Y={+1,−1},i=1,2,⋯,N;学习率
η
(
0
≤
η
≤
1
)
\eta(0\le \eta \le1)
η(0≤η≤1)。
输出:
w
,
b
w,b
w,b;感知机模型
f
(
x
)
=
s
i
g
n
(
w
⋅
x
+
b
)
f(x) = sign(w \cdot x + b)
f(x)=sign(w⋅x+b)。
- 选取初值 w 0 , b 0 w_0,b_0 w0,b0;
- 在训练集中选取数据 ( x i , y i ) (x_i,y_i) (xi,yi);
- i f y i ( w ⋅ x i + b ) ≤ 0 if \quad y_i(w\cdot x_i + b) \le 0 ifyi(w⋅xi+b)≤0,有 w ← w + η y i x i b ← b + η y i \begin{aligned}w \leftarrow w+\eta y_{i} x_{i} \\ b \leftarrow b+\eta y_{i} \end{aligned} w←w+ηyixib←b+ηyi
- 转到步骤2中,直到训练集中没有误分类点。
算法直观解释:当一个实例点被误分类,即位于分离超平面的错误一侧时,则调整
w
,
b
w,b
w,b的值,使分离超平面向该误分类点的一侧移动,以减少该误分类点与超平面之间的距离,直到超平面越过该误分类点使其被正确分类。
原始形式代码实现如下(采用鸢尾花数据集):
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
# 数据线性可分,二分类数据
# 此处为一元一次线性方程
class Perceptron(object):
# 类创建对象时自动执行,进行初始化操作
def __init__(self, data):
self.w = np.zeros(len(data[0]) - 1, dtype=np.float32) # 初始化权重
self.b = 0 # 初始化截距项
self.l_rate = 0.1 # 学习步长
# 定义符号函数
def sign(self, x, w, b):
y = np.dot(x, w) + b
return y
# 随机梯度下降法
def fit(self, X_train, Y_train):
sign = True
while sign:
sign = False
for d in range(len(X_train)):
X = X_train[d]
Y = Y_train[d]
if Y * self.sign(X, self.w, self.b) <= 0:
self.w = self.w + self.l_rate * np.dot(Y, X) # 更新权重
self.b = self.b + self.l_rate * Y # 更新步长
sign = True
if __name__ == '__main__':
# 加载鸢尾花数据集
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
# 列数据标注
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
# 选取特征和标签
data = np.array(df.iloc[:100, [0, 1, -1]])
X, Y = data[:, :-1], data[:, -1]
Y = np.array([1 if i == 1 else -1 for i in Y]) # 将label中的0标签替换为-1
perceptron = Perceptron(data) # 类的实例化,创建一个对象
perceptron.fit(X, Y) # 调用类的fit方法
# 可视化超平面
x = np.linspace(4, 7, 10) # linspace返回固定间隔的数据
y = -(perceptron.w[0] * x + perceptron.b) / perceptron.w[1] # 误差分类点到超平面的距离
plt.plot(x, y)
# 可视化展示
plt.plot(data[:50, 0], data[:50, 1], 'x', color='red', label='0')
plt.plot(data[50:100, 0], data[50:100, 1], 'o', color='black', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
输出如下:
原始形式代码实现2(采用李航统计学习课本案例):
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
class Perceptron(object):
def __init__(self, data):
self.w = np.zeros(len(data[0]) - 1, dtype=np.float32) # 初始化权重数组
self.b = 0 # 初始化截距项
self.l_rate = 0.1 # 学习步长
# 定义符号函数
def sign(self, x, w, b):
y = np.dot(x, w) + b
return y
# 随机梯度下降法
def fit(self, X_train, Y_train):
sign = True
while sign:
sign = False
for d in range(len(X_train)):
X = X_train[d]
Y = Y_train[d]
if Y * self.sign(X, self.w, self.b) <= 0:
self.w = self.w + self.l_rate * np.dot(Y, X) # 更新权重
self.b = self.b + self.l_rate * Y # 更新步长
sign = True
if __name__ == '__main__':
data = np.array([[3,3,1],[4,3,1],[1,1,-1]])
X, Y = data[:, :-1], data[:, -1]
perceptron = Perceptron(data)
perceptron.fit(X, Y)
# 可视化超平面
x = np.linspace(1, 4, 5) # linspace返回固定间隔的数据
y = -(perceptron.w[0] * x + perceptron.b) / perceptron.w[1] # 误差分类点到超平面的距离
plt.plot(x, y)
# 可视化展示
plt.plot(data[:2, 0], data[:2, 1], '+', color='red', label='good')
plt.plot(data[2:100, 0], data[2:, 1], 'o', color='black', label='bad')
plt.xlabel('x^(1)')
plt.ylabel('x^(2)')
plt.legend()
实现效果如下: