与公众号同步更新,详细内容及相关ipynb文件在公众号中,公众号:AI入门小白
感知机(perceptron) 是二类分类的线性分类模型,其输入为实例的特征向量,输出为实例的类别,取+1 和一1 二值。
感知机对应于输入空间(特征空间)中将实例划分为正负两类的分离超平面,属于判别模型。感知机学习旨在求出将训练数据进行线性划分的分离超平面。
感知机预测是用学习得到的感知机模型对新的输入实例进行分类。
感知机
定义2.1 (感知机) 假设输入空间(特征空间)是
x
⊆
R
n
x \subseteq R^n
x⊆Rn, 输出空间是
y
=
{
+
1
,
−
1
}
y=\{+1,-1\}
y={+1,−1}。输入
z
∈
X
z \in X
z∈X表示实例的特征向量,对应于输入空间(特征空间)的点;输出
y
∈
Y
y \in Y
y∈Y表示实例的类别。由输入空间到输出空间的如下函数:
f
(
x
)
=
sign
(
w
⋅
x
+
b
)
(2.1)
f(x)=\operatorname{sign}(w \cdot x+b)\tag{2.1}
f(x)=sign(w⋅x+b)(2.1)
称为感知机。其中,
ω
ω
ω和
b
b
b为感知机模型参数,
ω
∈
R
n
ω \in R^n
ω∈Rn叫作权值(weight) 或权值向量( weight vector ),
b
∈
R
b \in R
b∈R 叫作偏直( bias ) ,
ω
⋅
x
ω \cdot x
ω⋅x 表示
ω
ω
ω和
x
x
x的内积。
s
i
g
n
sign
sign是符号函数,即
s
i
g
n
(
x
)
=
{
+
1
,
x
≥
0
−
1
,
x
<
0
(2.2)
sign(x) = \begin{cases} +1,& x \geq 0 \\ -1,& x < 0 \end{cases}\tag{2.2}
sign(x)={+1,−1,x≥0x<0(2.2)
感知机学习策略
为了找出超平面,即确定感知机模型参数
ω
,
b
\omega , b
ω,b, 需要确定一个学习策略,即定义(经验)损失函数并将损失函数极小化。
损失函数是模型输出和观测结果间概率分布差异的量化。
感知机所采用的是损失函数的另一个选择,即误分类点到超平面
S
S
S的总距离,为此,首先写出输入空间
R
n
R^n
Rn中任一点
x
0
x_0
x0到超平面
S
S
S的距离:
1
∥
ω
∥
∣
ω
⋅
x
0
+
b
∣
\frac{1}{\parallel \omega \parallel} \mid \omega \cdot x_0 + b \mid
∥ω∥1∣ω⋅x0+b∣
这里
∥
ω
∥
\parallel \omega \parallel
∥ω∥是
ω
\omega
ω的
L
2
L_2
L2范数。
L 2 L_2 L2范数定义: ∥ x ∥ 2 = ∑ i = 1 n x i 2 \lVert x \rVert_2 = \sqrt{\sum^{n}_{i=1}x_i^2} ∥x∥2=i=1∑nxi2
其次,对于误分类的数据
(
x
i
,
y
i
)
(x_i , y_i )
(xi,yi) 来说,
−
y
i
(
ω
⋅
x
i
+
b
)
>
0
-y_i(\omega \cdot x_i + b) > 0
−yi(ω⋅xi+b)>0
成立。因此当
ω
⋅
x
i
+
b
>
0
\omega \cdot x_i + b > 0
ω⋅xi+b>0时,
y
i
=
−
1
y_i = -1
yi=−1;而当
ω
⋅
x
i
+
b
<
0
\omega \cdot x_i + b <0
ω⋅xi+b<0时,
y
i
=
+
1
y_i = +1
yi=+1。因此,误分类点
x
i
x_i
xi到超平面
S
S
S的距离是
−
1
∥
ω
∥
y
i
(
ω
⋅
x
i
+
b
)
-\frac{1}{\parallel \omega \parallel}y_i(\omega \cdot x_i + b)
−∥ω∥1yi(ω⋅xi+b)
这样,假设超平面
S
S
S的误分类点集合为
M
M
M,那么所有误分类点到超平面的总距离为
−
1
∥
ω
∥
∑
x
i
∈
M
y
i
(
ω
⋅
x
i
+
b
)
-\frac{1}{\parallel \omega \parallel}\sum_{x_i \in M}y_i(\omega \cdot x_i + b)
−∥ω∥1xi∈M∑yi(ω⋅xi+b)
不考虑
1
∥
ω
∥
\frac{1}{\parallel \omega \parallel}
∥ω∥1,就得到感知机学习的损失函数。
感知机
s
i
g
n
(
ω
⋅
x
+
b
)
sign(\omega \cdot x + b)
sign(ω⋅x+b)学习的损失函数定义为
L
(
ω
,
b
)
=
−
∑
x
i
∈
M
y
i
(
ω
⋅
x
i
+
b
)
(2.4)
L(\omega,b)=-\sum_{x_i \in M}y_i(\omega \cdot x_i + b)\tag{2.4}
L(ω,b)=−xi∈M∑yi(ω⋅xi+b)(2.4)
其中
M
M
M为误分类点的集合。这个损失函数就是感知机学习的经验风险系数。
显然,损失函数
L
(
ω
,
b
)
L(ω,b)
L(ω,b)的是非负的。如果没有误分类点,损失函数值是0。而且,误分类点越少,误分类点离超平面越近,损失函数值就越小。
一个特定的样本点的损失函数:在误分类时是参数
ω
,
b
ω , b
ω,b的线性函数,在正确分类时是0。因此,给定训练数据集
T
T
T, 损失函数
L
(
ω
,
b
)
L(\omega, b)
L(ω,b) 是
ω
,
b
ω , b
ω,b 的连续可导函数。
感知机学习算法的原始形式
算法2.1 ( 感知机学习算法的原始形式)
输入:训练数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
⋯
,
(
x
N
,
y
N
)
}
T = \lbrace (x_1,y_1),(x_2,y_2),\cdots,(x_N,y_N) \rbrace
T={(x1,y1),(x2,y2),⋯,(xN,yN)},其中
x
i
∈
X
=
R
n
,
y
i
∈
Y
=
{
−
1
,
+
1
}
,
i
=
1
,
2
,
⋯
,
N
x_i \in X = R^n, y_i \in Y = \{ -1, +1 \}, i = 1,2,\cdots,N
xi∈X=Rn,yi∈Y={−1,+1},i=1,2,⋯,N;学习率
η
(
0
<
η
≤
1
)
;
\eta(0 < \eta \leq 1);
η(0<η≤1);
输出: ω , b ; \omega , b; ω,b; 感知机模型 f ( x ) = s i g n ( ω ⋅ x + b ) 。 f(x) = sign(\omega \cdot x + b)。 f(x)=sign(ω⋅x+b)。
(1)选取初值
ω
0
,
b
0
;
\omega_0,b_0;
ω0,b0;
(2) 在训练集中选取数据
(
x
i
,
y
i
)
;
(x_i,y_i);
(xi,yi);
(3) 如果
y
i
(
ω
⋅
x
i
+
b
)
≤
0
,
y_i(\omega \cdot x_i + b) \leq 0,
yi(ω⋅xi+b)≤0,
ω
←
ω
+
η
y
i
x
i
\omega \leftarrow \omega + \eta y_ix_i
ω←ω+ηyixi
b
←
b
+
η
y
i
b \leftarrow b + \eta y_i
b←b+ηyi
(4) 转至(2) ,直至训练集中没有误分类点。
这种学习算法直观上有如下解释:当一个实例点被误分类,即位于分离超平面的错误一侧时,则调整
ω
,
b
\omega,b
ω,b的值,使分离超平面向该误分类点的一侧移动,以减少该误分类点与超平面间的距离,直至超平面越过该误分类点使其被正确分类。
算法2.1 是感知机学习的基本算法,对应于后面的对偶形式,称为原始形式。感知机学习算法简单且易于实现。
感知机学习:不断带入测试集求出 ω , b \omega,b ω,b
感知机学习算法的对偶形式
算法2.2 ( 感知机学习算法的对偶形式)
输入:线性可分的数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯ , ( x N , y N ) } T=\{(x_1, y_1), (x_2, y_2), \cdots, (x_N, y_N)\} T={(x1,y1),(x2,y2),⋯,(xN,yN)},其中 x i ∈ R n , y i ∈ { − 1 , + 1 } , i = 1 , 2 , ⋯ , N ; x_i \in R^n, y_i \in \{-1, +1\}, i = 1,2,\cdots,N; xi∈Rn,yi∈{−1,+1},i=1,2,⋯,N;学习率 η ( 0 < η ≤ 1 ) ; \eta (0 < \eta \leq 1); η(0<η≤1);
输出:
α
,
b
;
\alpha, b;
α,b;感知机模型
f
(
x
)
=
s
i
g
n
(
∑
j
=
1
N
α
j
y
j
x
j
⋅
x
+
b
)
f(x) = sign\Bigg( \sum^N_{j=1}\alpha_j y_j x_j \cdot x + b \Bigg)
f(x)=sign(j=1∑Nαjyjxj⋅x+b)其中
α
=
(
α
1
,
α
2
,
⋯
,
α
N
)
T
\alpha = (\alpha_1,\alpha_2,\cdots,\alpha_N)^T
α=(α1,α2,⋯,αN)T。
(1)
α
←
0
,
b
←
0
;
\alpha \leftarrow 0, b \leftarrow 0;
α←0,b←0;
(2)在训练集中选取数据
(
x
i
,
y
i
)
;
(x_i,y_i);
(xi,yi);
(3)若
y
i
(
∑
j
=
1
N
α
j
y
j
x
j
⋅
x
i
+
b
)
≤
0
y_i \Bigg (\sum^N_{j=1} \alpha_j y_j x_j \cdot x_i + b \Bigg) \leq 0
yi(j=1∑Nαjyjxj⋅xi+b)≤0
α
←
α
i
+
η
\alpha \leftarrow \alpha_i + \eta
α←αi+η
b
←
b
+
η
y
i
b \leftarrow b + \eta y_i
b←b+ηyi
(4)转至(2)直到没有误分类数据。
对偶形式中训练实例仅以内积的形式出现。为了方便,可以预先将训练集中实例间的内积计算出来并以矩阵的形式存储,这个矩阵就是所谓的Gram 矩阵(Gram
matrix)
G
=
[
x
i
⋅
x
j
]
N
×
N
G = [x_i \cdot x_j]_{N \times N}
G=[xi⋅xj]N×N
拿出iris数据集中两个分类的数据和[sepal length,sepal width]作为特征
代码部分
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
%matplotlib inline
# load data
iris = load_iris() #加载并返回鸢尾花数据集,Iris 鸢尾花数据集是一个经典数据集
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target # iris.data 包含了四个特征值,iris.target为目标值
df.columns = [
'sepal length', 'sepal width', 'petal length', 'petal width', 'label'
]
df.label.value_counts()
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend() # 在绘图时传入label参数,在此之后,你可以调⽤ax.legend()或plt.legend()来⾃动创建图例
data = np.array(df.iloc[:100, [0, 1, -1]]) # 正序从0开始排序,倒序从-1开始排序,此处索引为DataFrame的前100行的第0,1,-1列
X, y = data[:,:-1], data[:,-1]
y = np.array([1 if i == 1 else -1 for i in y])
感知机算法
# 数据线性可分,二分类数据
# 此处为一元一次线性方程
class Model:
def __init__(self):
self.w = np.ones(len(data[0]) - 1, dtype=np.float32) # 得到两个1;因为训练集中x有两位;两个x对应两个w
self.b = 0
self.l_rate = 0.1 # 学习率
# self.data = data
def sign(self, x, w, b):
y = np.dot(x, w) + b # dot计算矩阵内积
return y
# 随机梯度下降法
def fit(self, X_train, y_train):
is_wrong = False
while not is_wrong:
wrong_count = 0
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: # 如果 𝑦𝑖(𝜔⋅𝑥𝑖+𝑏)≤0
self.w = self.w + self.l_rate * np.dot(y, X) # 更新w
self.b = self.b + self.l_rate * y # 更新b
wrong_count += 1 # 记误分类数
if wrong_count == 0: # 不断循环直到误分类数为0
is_wrong = True # 更新is_wrong,跳出while循环
return 'Perceptron Model!'
def score(self):
pass
perceptron = Model()
perceptron.fit(X, y) # 得到的感知机模型为sign(w[0] * x_1 + w[1] * x_2 + b)
x_points = np.linspace(4, 7, 10) # 用来创建等差数列,开始点,结束点,样本数据量
# 分离超平面为w[0] * x_1 + w[1] * x_2 + b = 0;给出一组x_1得出另一组x_2
y_ = -(perceptron.w[0] * x_points + perceptron.b) / perceptron.w[1]
plt.plot(x_points, y_)
plt.plot(data[:50, 0], data[:50, 1], 'bo', color='blue', label='0')
plt.plot(data[50:100, 0], data[50:100, 1], 'bo', color='orange', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
以sklearn为实例
import sklearn
from sklearn.linear_model import Perceptron
clf = Perceptron(fit_intercept=True, # 默认为True,是否应该估计截距。如果为False,则假定数据已经居中
max_iter=1000, # 最大训练次数,默认为1000,它只会影响fit方法中的行为,而不会影响 partial_fit方法
shuffle=True) # 在每个时期之后是否应重新整理训练数据
clf.fit(X, y) # 用随机梯度下降拟合线性模型
#指定给特征的权重。
print(clf.coef_)
# 截距 决策函数中的常数
print(clf.intercept_)
# 画布大小
plt.figure(figsize=(10, 10))
# 中文标题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.title('鸢尾花线行数据示例')
plt.scatter(data[:50, 0], data[:50, 1], c = 'b', label='Iris-setosa')
plt.scatter(data[50:100, 0], data[50:100, 1], c='orange', label='Iris-versicolor')
# 画感知机的线
x_ponits = np.arange(4, 8)
y_ = -(clf.coef_[0][0] * x_ponits + clf.intercept_) / clf.coef_[0][1]
plt.plot(x_ponits, y_)
# 其他部分
plt.legend() # 显示图例
plt.grid(False) # 不显示网格
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
注意 !
在上图中,有一个位于左下角的蓝点没有被正确分类,这是因为 SKlearn 的 Perceptron 实例中有一个tol
参数。
tol
参数规定了如果本次迭代的损失和上次迭代的损失之差小于一个特定值时,停止迭代。所以我们需要设置 tol=None
使之可以继续迭代:
clf = Perceptron(fit_intercept=True,
max_iter=1000,
tol=None,
shuffle=True)
clf.fit(X, y)
# 画布大小
plt.figure(figsize=(10, 10))
# 中文标题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.title('鸢尾花线性数据示例')
plt.scatter(data[:50, 0], data[:50, 1], c='b', label='Iris-setosa')
plt.scatter(data[50:100, 0], data[50:100, 1], c='orange', label='Iris-versicolor')
# 画感知机的线
x_ponits = np.arange(4, 8)
y_ = -(clf.coef_[0][0] * x_ponits + clf.intercept_) / clf.coef_[0][1]
plt.plot(x_ponits, y_)
# 其他部分
plt.legend() # 显示图例
plt.grid(False) # 不显示网格
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
两种鸢尾花都被正确分类。
数据来源:统计学习方法(第二版) &
https://github.com/fengdu78/lihang-code