基础知识:
1、感知机学习的模型
感知机是根据输入实例的特征向量x对其进行二分分类的线性分类模型:
f(x) = sign(w.x + b)
感知机模型对应于输入空间中的超平面w.x + b = 0,也即感知机学习的目的就是:找到一个能够完美划分不同类别的实例点的超平面;
2、感知机学习的策略
感知机的学习策略就是极小化损失函数:
损失函数对应于误分类点到分离超平面的总距离,换句话说,分离超平面被误分类点所吸引,不断向其靠近,最终完成划分任务;
3、感知机学习的算法
首先任意选取一个超平面,然后使用梯度下降法不断极小化目标函数(也就是我们上面的损失函数),直到最终完成划分。
在梯度下降法的过程中,我们每一次都要随机选取一个误分类点,使其梯度下降
具体过程如下:
(1)是任意选取一个分离超平面;(2)是随机选取一个误分类点,这里使用代码实现的时候我们就直接遍历加if判断即可;(3)就是对误分类点使用梯度下降法,减小其梯度;(4)即为不断重复前三步,最终完成划分。
4、算法的收敛性
当训练数据集线性可分时,感知机学习算法是收敛的,感知机算法在训练数据集上的误分类次数满足不等式:
k <= (R/y)^2
也就是说不管我们尝试多少次,总能找到一种完美划分的方法,且最终的结果也并不是唯一的,不同的出事分离超平面,不同的误分类点选取顺序,都有可能对结果产生影响,所以我们有无穷多个解。
代码实现:
# 训练集: # vector(第一项是截距项) label # ------------------------------------------ # [1, 1, 4] 1 # [1, 2, 3] 1 # [1, -2, 3] 1 # [1, -2, 2] 0 # [1, 0, 1] 0 # [1, 1, 2] 0 # 测试集: # vector(第一项是截距项) label # ------------------------------------------ # [1, 1, 1] ? # [1, 2, 0] ? # [1, 2, 4] ? # [1, 1, 3] ? from numpy import * import matplotlib.pyplot as plt import operator import time # 数据集 def createTrainDataSet(): trainDataMat = [[1, 1, 4], [1, 2, 3], [1, -2, 3], [1, -2, 2], [1, 0, 1], [1, 1, 2]] trainClassLable = [1, 1, 1, -1, -1, -1] # 分类标签,也即权重向量 w return trainDataMat, trainClassLable # 测试集 def createTestDataSet(): testDataMat = [[1, 1, 1], [1, 2, 0], [1, 2, 4], [1, 1, 3]] return testDataMat # 核心代码PLA感知机算法 def pla(dataMatIn, classLabels): # mat函数将数组转化为矩阵,transpose函数将矩阵进行转置 dataMatrix = mat(dataMatIn) labelMat = mat(classLabels).transpose() m, n = shape(dataMatrix) # 获取矩阵的行、列数 weights = ones((n, 1)) # 设置初始权重为 [1 1 1] while True: Done = True for i in range(m): if (dot(dataMatrix[i], weights) * labelMat[i] > 0): # dot函数计算矩阵的内积,此时计算yi(w*xi + b) <= 0 是否成立 continue # 如果该点误分类,则梯度下降 else: Done = False weights += (labelMat[i] * dataMatrix[i]).transpose() if Done: break return weights # 打印感知机划分图像 def plotBestFit(weights): dataMat, labelMat = createTrainDataSet() dataArr = array(dataMat) n = shape(dataArr)[0] xcord1 = []; ycord1 = [] xcord2 = []; ycord2 = [] for i in range(n): if int(labelMat[i]) == 1: xcord1.append(dataArr[i, 1]) ycord1.append(dataArr[i, 2]) else: xcord2.append(dataArr[i, 1]) ycord2.append(dataArr[i, 2]) fig = plt.figure() ax = fig.add_subplot(111) # 画出训练数据集中的点 ax.scatter(xcord1, ycord1, s=30, c='red', marker='s') ax.scatter(xcord2, ycord2, s=30, c='green') # 画出分界线,也即分离超平面 x = arange(-3.0, 3.0, 0.1) y = (-weights[0] - weights[1] * x) / weights[2] ax.plot(x, y) plt.xlabel('X1'); plt.ylabel('X2') plt.show() # 判断测试数据集中的向量在分离超平面的哪一边,大于零说明在上面,返回标签1;小于零说明在下面,返回标签-1 def classifyVector(inX, weights): if float(dot(inX, weights)) > 0: return 1 else: return -1 # 对测试数据集中的向量进行预测分类 def Predict(dataSet, weights): predict = [] for vector in dataSet: predict.append(classifyVector(vector, weights)) return predict def main(): trainDataSet, trainShares = createTrainDataSet() testDataSet = createTestDataSet() # 输出最终的分离超平面 Final_weights = pla(trainDataSet, trainShares) print("Final_weights = \n", Final_weights) plotBestFit(Final_weights) # 输出对测试数据集的预测结果 predictShares = Predict(testDataSet, Final_weights) print("predictResult: \n", predictShares) if __name__ == '__main__': start = time.clock() main() end = time.clock() print('finish all in %s' % str(end - start))
输出结果如下:
可以看到分离超平面实现了对训练数据集中向量的完美划分,且对测试集中数据点的预测也是准确的,算法用时0.37s还可以,我们的PLA感知机算法就实现了。