1.感知机是根据输入实例的特征向量 x x x对其进行二类分类的线性分类模型:
f ( x ) = sign ( w ⋅ x + b ) f(x)=\operatorname{sign}(w \cdot x+b) f(x)=sign(w⋅x+b)
感知机模型对应于输入空间(特征空间)中的分离超平面 w ⋅ x + b = 0 w \cdot x+b=0 w⋅x+b=0。
2.感知机学习的策略是极小化损失函数:
min w , b L ( w , b ) = − ∑ x i ∈ M y i ( w ⋅ x i + b ) \min _{w, b} L(w, b)=-\sum_{x_{i} \in M} y_{i}\left(w \cdot x_{i}+b\right) w,bminL(w,b)=−xi∈M∑yi(w⋅xi+b)
损失函数对应于误分类点到分离超平面的总距离。
3.感知机学习算法是基于随机梯度下降法的对损失函数的最优化算法,有原始形式和对偶形式。算法简单且易于实现。原始形式中,首先任意选取一个超平面,然后用梯度下降法不断极小化目标函数。在这个过程中一次随机选取一个误分类点使其梯度下降。
4.当训练数据集线性可分时,感知机学习算法是收敛的。感知机算法在训练数据集上的误分类次数 k k k满足不等式:
k ⩽ ( R γ ) 2 k \leqslant\left(\frac{R}{\gamma}\right)^{2} k⩽(γR)2
当训练数据集线性可分时,感知机学习算法存在无穷多个解,其解由于不同的初值或不同的迭代顺序而可能有所不同。
f ( x ) = s i g n ( w ⋅ x + b ) f(x) = sign(w\cdot x + b) f(x)=sign(w⋅x+b)
sign ( x ) = { + 1 , x ⩾ 0 − 1 , x < 0 \operatorname{sign}(x)=\left\{\begin{array}{ll}{+1,} & {x \geqslant 0} \\ {-1,} & {x<0}\end{array}\right. sign(x)={+1,−1,x⩾0x<0
给定训练集:
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), \cdots,\left(x_{N}, y_{N}\right)\right\} T={(x1,y1),(x2,y2),⋯,(xN,yN)}
定义感知机的损失函数
L ( w , b ) = − ∑ x i ∈ M y i ( w ⋅ x i + b ) L(w, b)=-\sum_{x_{i} \in M} y_{i}\left(w \cdot x_{i}+b\right) L(w,b)=−∑xi∈Myi(w⋅xi+b)
随即梯度下降法 Stochastic Gradient Descent
随机抽取一个误分类点使其梯度下降。
若 y i ( w ⋅ x i + b ) ⩽ 0 y_{i}\left(w \cdot x_{i}+b\right)\leqslant 0 yi(w⋅xi+b)⩽0
则判断为误分类点,然后进行w和b的更新:
w = w + η y i x i w = w + \eta y_{i}x_{i} w=w+ηyixi
b = b + η y i b = b + \eta y_{i} b=b+ηyi
当实例点被误分类,即位于分离超平面的错误侧,则调整 w w w, b b b的值,使分离超平面向该无分类点的一侧移动,直至误分类点被正确分类,其中 η \eta η为学习率
实现1将手动实现感知机算法,数据集则采用sklearn中鸢尾花数据集。
数据集介绍:iris数据集由Fisher在1936年整理,包含4个特征(Sepal.Length(花萼长度)、Sepal.Width(花萼宽度)、Petal.Length(花瓣长度)、Petal.Width(花瓣宽度)),特征值都为正浮点数,单位为厘米。目标值为鸢尾花的分类(Iris Setosa(山鸢尾)、Iris Versicolour(杂色鸢尾),Iris Virginica(维吉尼亚鸢尾))。
为了实现感知机算法,本文拿出iris数据集中两个分类的数据和[sepal length,sepal width]作为特征,去识别出花的类型。
实现1:
# @Author: coderdz
# @File: Perceptron.py
# @Site: github.com/dingzhen7186
# @Ended Time : 2020/9/21
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import load_iris
class Perceptron():
def __init__(self):
# w初始化为全1数组
self.w = np.ones(len(data[0]) - 1, dtype = np.float32)
self.b = 0
self.rate = 0.5 # 初始化学习率
# 感知机训练, 找出最合适的w, b
def fit(self, x_train, y_train):
while True:
flag = True # 标记是否存在误分类数据
for i in range(len(x_train)): # 遍历训练数据
xi = x_train[i]
yi = y_train[i]
# 判断 yi * (wx + b) <= 0
if yi * (np.inner(self.w, xi) + self.b) <= 0:
flag = False # 找到误分类数据, flag标记为False
# 更新w, b值
self.w += self.rate * np.dot(xi, yi)
self.b += self.rate * yi
if flag:
break
# 输出w = ? , b = ?
print('w = ' + str(self.w) + ', b = ' + str(self.b))
# 图形显示结果
def show(self, data):
x_ = np.linspace(4, 7, 10)
y_ = -(self.w[0] * x_ + self.b) / self.w[1]
# 画出这条直线
plt.plot(x_, y_)
# 画出数据集的散点图
plt.plot(data[:50, 0], data[:50, 1], 'bo', c = 'blue', label = '0')
plt.plot(data[50:100, 0], data[50:100, 1], 'bo', c = 'orange', label = '1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()
iris = load_iris()
# 通过DataFrmae对象获取iris数据集对象, 列名为该数据集样本的特征名
df = pd.DataFrame(iris.data, columns = iris.feature_names)
# 增加label列为它们的分类标签
df['label'] = iris.target
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
#print(df.label.value_counts()) 不同标签的样本数量
# 2:50 1:50 0:50
# 画出散点图查看特征点的分布
plt.scatter(df[:50]['sepal length'].values, df[:50]['sepal width'].values, label='0')
plt.scatter(df[50:100]['sepal length'].values, df[50:100]['sepal width'].values, label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()
# 选择数据集
data = np.array(df.iloc[:100, [0, 1, -1]])
# 数据集划分
x, y = data[:, :-1], data[:, -1]
# 将y数据集值变为1和-1
y = np.array([1 if i == 1 else -1 for i in y])
# 开始训练
p = Perceptron()
p.fit(x, y)
p.show(data)
输出如下:
w = [ 39.85006 -50.400837], b = -63.5
实现2将采用sklearn中已经实现好了的Perceptron感知机,然后调用相关方法实现二分类。
实现2:
# @Author: coderdz
# @File: Perceptron.py
# @Site: github.com/dingzhen7186
# @Ended Time : 2020/9/21
import sklearn
from sklearn.linear_model import Perceptron
from sklearn.datasets import load_iris
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# 数据预处理
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])
# 模型训练
clf = Perceptron(fit_intercept = True, max_iter = 1000, tol = None, shuffle = True)
clf.fit(x, y)
# 输出参数w, b
print('w = ' + str(clf.coef_) + ', b = ' + str(clf.intercept_))
# 画出图形
plt.figure(figsize = (10, 10)) #设置画布大小
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.title('鸢尾花线性数据示例')
# 画出散点图, iris-setosa:山鸢尾, iris-versicolour: 杂色鸢尾
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_ = np.arange(4, 8)
y_ = -(clf.coef_[0][0]*x_ + clf.intercept_) / clf.coef_[0][1]
plt.plot(x_, y_)
plt.legend() # 显示图例
plt.grid(False) # 不显示网络
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.show()
输出如下:
w = [[ 66.1 -83.3]], b = [-106.]