什么是感知机「Perceptron」
PLA全称是Perceptron Linear Algorithm,即线性感知机算法,属于一种最简单的感知机(Perceptron)模型。
感知机模型是机器学习二分类问题中的一个非常简单的模型。它的基本结构如下图所示:
其中,x
i
xi是输入,w
i
wi表示权重系数,b
b表示偏移常数。感知机的线性输出为:
scores=∑
i
N
w
i
x
i
b
scores=∑iNwixi b
为了简化计算,通常我们将b
b作为权重系数的一个维度,即w
0
w0。同时,将输入x
x扩展一个维度,为1。这样,上式简化为:
scores=∑
i
N 1
w
i
x
i
scores=∑iN 1wixi
scores
scores是感知机的输出,接下来就要对scores
scores进行判断:
- 若scores≥0
- scores≥0,则y
- ^
- =1
- y^=1(正类)
- 若scores<0
- scores<0,则y
- ^
- =−1
- y^=−1(负类)
以上就是线性感知机模型的基本概念,简单来说,它由线性得分计算和阈值比较两个过程组成,最后根据比较结果判断样本属于正类还是负类。
PLA理论解释
对于二分类问题,可以使用感知机模型来解决。PLA的基本原理就是逐点修正,首先在超平面上随意取一条分类面,统计分类错误的点;然后随机对某个错误点就行修正,即变换直线的位置,使该错误点得以修正;接着再随机选择一个错误点进行纠正,分类面不断变化,直到所有的点都完全分类正确了,就得到了最佳的分类面。
利用二维平面例子来进行解释,第一种情况是错误地将正样本(y=1)分类为负样本(y=-1)。此时,wx<0
wx<0,即w
w与x
x的夹角大于90度,分类线l
l的两侧。修正的方法是让夹角变小,修正w
w值,使二者位于直线同侧:
w:=w x=w yx
w:=w x=w yx
修正过程示意图如下所示:
第二种情况是错误地将负样本(y=-1)分类为正样本(y=1)。此时,wx>0
wx>0,即w
w与x
x的夹角小于90度,分类线l
l的同一侧。修正的方法是让夹角变大,修正w
w值,使二者位于直线两侧:
w:=w−x=w yx
w:=w−x=w yx
修正过程示意图如下所示:
经过两种情况分析,我们发现PLA每次w
w的更新表达式都是一样的:w:=w yx
w:=w yx。掌握了每次w
w的优化表达式,那么PLA就能不断地将所有错误的分类样本纠正并分类正确。
数据准备
导入数据
数据集存放在’../data/’目录下,该数据集包含了100个样本,正负样本各50,特征维度为2。
import numpy as np import pandas as pd data = pd.read_csv('./data/data1.csv', header=None) # 样本输入,维度(100,2) X = data.iloc[:,:2].values # 样本输出,维度(100,) y = data.iloc[:,2].values 1 2 3 4 5 6 7 8
数据分类与可视化
下面我们在二维平面上绘出正负样本的分布情况。
import matplotlib.pyplot as plt plt.scatter(X[:50, 0], X[:50, 1], color='blue', marker='o', label='Positive') plt.scatter(X[50:, 0], X[50:, 1], color='red', marker='x', label='Negative') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.legend(loc = 'upper left') plt.title('Original Data') plt.show() 1 2 3 4 5 6 7 8 9
PLA算法
特征归一化
首先分别对两个特征进行归一化处理,即:
X=X−μ
σ
X=X−μσ
其中,μ
μ是特征均值,σ
σ是特征标准差。
# 均值 u = np.mean(X, axis=0) # 方差 v = np.std(X, axis=0) X = (X - u) / v # 作图 plt.scatter(X[:50, 0], X[:50, 1], color='blue', marker='o', label='Positive') plt.scatter(X[50:, 0], X[50:, 1], color='red', marker='x', label='Negative') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.legend(loc = 'upper left') plt.title('Normalization data') plt.show() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
直线初始化
# X加上偏置项 X = np.hstack((np.ones((X.shape[0],1)), X)) # 权重初始化 w = np.random.randn(3,1) 1 2 3 4
显示初始化直线位置:
# 直线第一个坐标(x1,y1) x1 = -2 y1 = -1 / w[2] * (w[0] * 1 w[1] * x1) # 直线第二个坐标(x2,y2) x2 = 2 y2 = -1 / w[2] * (w[0] * 1 w[1] * x2) # 作图 plt.scatter(X[:50, 1], X[:50, 2], color='blue', marker='o', label='Positive') plt.scatter(X[50:, 1], X[50:, 2], color='red', marker='x', label='Negative') plt.plot([x1,x2], [y1,y2],'r') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.legend(loc = 'upper left') plt.show() 1 2 3 4 5 6 7 8 9 10 11 12 13 14
由上图可见,一般随机生成的分类线,错误率很高。
计算scores,更新权重
接下来,计算scores,得分函数与阈值0做比较,大于零则y
^
=1
y^=1,小于零则y
^
=−1
y^=−1
s = np.dot(X, w) y_pred = np.ones_like(y) # 预测输出初始化 loc_n = np.where(s < 0)[0] # 大于零索引下标 y_pred[loc_n] = -1 1 2 3 4
接着,从分类错误的样本中选择一个,使用PLA更新权重系数w
w。
# 第一个分类错误的点 t = np.where(y != y_pred)[0][0] # 更新权重w w = y[t] * X[t, :].reshape((3,1)) 1 2 3 4
迭代更新训练
更新权重w
w是个迭代过程,只要存在分类错误的样本,就不断进行更新,直至所有的样本都分类正确。(注意,前提是正负样本完全可分)
for i in range(100): s = np.dot(X, w) y_pred = np.ones_like(y) loc_n = np.where(s < 0)[0] y_pred[loc_n] = -1 num_fault = len(np.where(y != y_pred)[0]) print('第-次更新,分类错误的点个数:-' % (i, num_fault)) if num_fault == 0: break else: t = np.where(y != y_pred)[0][0] w = y[t] * X[t, :].reshape((3,1)) 1 2 3 4 5 6 7 8 9 10 11 12
迭代完毕后,得到更新后的权重系数w
w,绘制此时的分类直线是什么样子。
# 直线第一个坐标(x1,y1) x1 = -2 y1 = -1 / w[2] * (w[0] * 1 w[1] * x1) # 直线第二个坐标(x2,y2) x2 = 2 y2 = -1 / w[2] * (w[0] * 1 w[1] * x2) # 作图 plt.scatter(X[:50, 1], X[:50, 2], color='blue', marker='o', label='Positive') plt.scatter(X[50:, 1], X[50:, 2], color='red', marker='x', label='Negative') plt.plot([x1,x2], [y1,y2],'r') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.legend(loc = 'upper left') plt.show() 1 2 3 4 5 6 7 8 9 10 11 12 13 14
其实,PLA算法的效率还算不错,只需要数次更新就能找到一条能将所有样本完全分类正确的分类线。所以得出结论,对于正负样本线性可分的情况,PLA能够在有限次迭代后得到正确的分类直线。
总结与疑问
这里导入的数据本身就是线性可分的,可以使用PCA来得到分类直线。但是,如果数据不是线性可分,即找不到一条直线能够将所有的正负样本完全分类正确,这种情况下,似乎PCA会永远更新迭代下去,却找不到正确的分类线。
,