前言
感知机算法是一个比较古老的机器学习算法了,是Rosenblatt在1957年提出的,是神经网络和支持向量机的基础。感知机算法只能解决线性分类模型。
算法原理
1. 感知机算法的原始形式
感知机模型可以表示为:f(x)=sign(w*x+b)
其中w为权值,b为偏置,w * x表示内积,sign为符号函数。然后我们需要 建立误分类的损失函数,误分类点到超平面的总距离,损失函数是连续可导函数。损失函数表示为:
感知机算法的目标就是要最小化这个损失函数,使得误分类点个数为0,这也要求数据集是线性可分的。感知机算法的算法过程如下图所示:
这个算法过程中的梯度更新比较难理解,需要推导一下,过程如下(从南瓜书截图):
然后从李航的《统计学习方法》中可以知道,如果数据集是线性可分的,那么感知算法一定会收敛,并且误分类次数是有上界的,有兴趣可以取看一下这个不等式推导。
2.感知机算法的对偶形式
代码实现
这里拿出iris数据集中的两个分类的数据,并以[sepal length,sepal width]作为特征,这里实现感知机算法的原始形式。实验代码如下:
#coding=utf-8
from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 加载鸢尾花数据集
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']
# print (df.label.value_counts())
print(df.head(10))
# 数据可视化
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], c='red', label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], c='blue', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
#plt.show()
# 选择特征和标签
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
# 开始实现感知机算法
class Model:
# 初始化
def __init__(self):
# 初始化权重
self.w = np.ones(len(data[0]) - 1, dtype=np.float32)
# 初始化偏执
self.b = 0
# 学习率
self.l_rate = 0.1
# 定义符号函数sign
def sign(self, x, w, b):
y = np.dot(x, w) + b
return y
# 随机梯度下降法
def fit(self, X_train, y_train):
is_wrong = False
while not is_wrong:
wrong_cnt = 0
for i in range(len(X_train)):
X = X_train[i]
y = y_train[i]
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
wrong_cnt += 1
if(wrong_cnt == 0):
is_wrong = True
return 'Perceptron Model!'
def score(self):
pass
# 开始调用感知机模型
perceptron = Model()
perceptron.fit(X, y)
# 可视化超平面
x_points = np.linspace(4, 7, 10)
# 误分类点到超平面的距离
y_ = -(perceptron.w[0] * x_points + perceptron.b) / perceptron.w[1]
plt.plot(x_points, y_)
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], c='red', label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], c='blue', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.show()
结果图
可以看到数据已经被学习到的直线完全分开了,说明我们的感知机算法在线性分类问题中的有效性。