最近开始复习李航的统计学习方法啦,所以通过博客的形式系统整理所学并督促自己,重心主要放在算法的Python实现上,基础知识的介绍可能相对简洁一点,第一次发,以后在内容和格式上都会慢慢改进.
第2章 感知机
2.1 感知机原理
2.1.1 感知机的模型定义
感知机是二类分类的线性分类模型,参考书上的定义即可,简洁明了,其输入为实例的特征向量,输出为实例的类别,取+1和-1二值. 感知机属于判别模型,其学习旨在求出将训练数据进行线性划分的分离超平面.
2.1.2 感知机的学习策略
感知机的学习策略需要一个假设前提,即训练数据集是线性可分的,数据集的线性可分性可以简单归纳为:存在某个形如 w ⋅ x + b = 0 w·x+b=0 w⋅x+b=0的超平面 S S S,能够将数据集的正实例点和负实例点完全正确地划分到超平面的两侧.
所谓确定一个学习策略,即定义(经验)损失函数并将损失函数极小化,感知机所采用的损失函数是误分类点到超平面
S
S
S的总距离. 制定学习策略的步骤如下:
(1) 写出输入空间Rn中任意点
x
i
x_i
xi到超平面
S
S
S的距离
1
∥
w
∥
∣
w
⋅
x
0
+
b
∣
\frac{1}{\parallel w \parallel} \mid w·x_0+b \mid
∥w∥1∣w⋅x0+b∣这里
∥
w
∥
\parallel w \parallel
∥w∥是
w
w
w的
L
2
L_2
L2范数.
(2) 对于误分类的数据
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi),则有
−
y
i
(
w
⋅
x
i
+
b
)
>
0
-y_i(w·x_i+b)>0
−yi(w⋅xi+b)>0成立,因此误分类点
x
i
x_i
xi到超平面
S
S
S的距离是
−
1
∥
w
∥
y
i
(
w
⋅
x
i
+
b
)
-\frac{1}{\parallel w \parallel} y_i(w·x_i+b)
−∥w∥1yi(w⋅xi+b)不考虑
1
∥
w
∥
\frac{1}{\parallel w \parallel}
∥w∥1,就得到感知机学习的损失函数.
(3) 给定训练数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
.
.
.
,
(
x
N
,
y
N
)
}
T=\{ (x_1,y_1),(x_2,y_2),...,(x_N,y_N) \}
T={(x1,y1),(x2,y2),...,(xN,yN)}感知机学习的经验风险函数为
L
(
w
,
b
)
=
−
∑
x
i
∈
M
y
i
(
w
⋅
x
i
+
b
)
L(w,b)=-\displaystyle\sum_{x_i \in M}y_i(w·x_i+b)
L(w,b)=−xi∈M∑yi(w⋅xi+b)其中
M
M
M为误分类点的集合.
2.1.3 感知机学习算法
感知机的学习算法包含了原始形式和对偶形式两种,分别定义如下:
2.2 感知机Python实现
本章的习题描述极为简单,那么我就使用Python实现本章对应感知机原始形式与对偶形式的两个例题:例2.1、例2.2. 废话不多说,直接贴代码会更加简洁直观.
2.2.1 原始形式Python代码
# -*- coding: utf-8 -*-
# 感知机的原始形式
import numpy as np
import matplotlib.pyplot as plt
class Perceptron(object):
def __init__(self,eta=1,iter=50):
# eta:学习率;itea:最大迭代次数
self.eta = eta
self.iter = iter
# 根据现有权值和偏置预测分类
def predict(self,xi,w,b):
target = np.dot(w,xi)+b
return target
# 迭代修正权值和偏置
def interation(self,vector,label):
"""
input:
param vector: 训练数据向量
param label: 训练数据的原始划分类别
return:
weight and bias: 学习到的权值与偏置
"""
data_shape = vector.shape
print("data_shape",data_shape)
# 初始化权值为零向量
self.weight = np.zeros(data_shape[1])
# 初始偏置
self.bias = 0
# 记录每一轮迭代还没有误分类数据
errors_point = 0
# True表示还需要继续迭代
check_inter = True
n = 0
print("迭代次数:%d , 初始权值:%s , 初始偏置:%s" % (n,str(self.weight), str(self.bias)))
while check_inter and n<self.iter:
n +=1
errors_point = 0
for xi,yi in zip(vector,label):
xi_prediction = self.predict(xi,self.weight,self.bias)
# 感知机中的判断数据有没有被误分类的公式
if yi*xi_prediction <= 0:
# 修正权值和偏置
self.weight = self.weight+self.eta*xi*yi
self.bias = self.bias+self.eta*yi
errors_point +=1
print("迭代次数:%d , 误分类点:%s , 权值:%s , 偏置:%s" % (n,str(xi),str(self.weight),str(self.bias)))
break
if errors_point:
# 如果还存在误分类点,则继续迭代
check_inter = True
else:
# 如果不存误分类点,则停止迭代
check_inter = False
print("权值:%s , 偏置:%s" % (str(self.weight),str(self.bias)))
return self.weight,self.bias
def plot_graph(self,vector,label,weight,bias):
plt.figure()
for i in range(len(vector)):
if label[i] == 1:
plt.plot(vector[i][0],vector[i][1],'ro')
else:
plt.plot(vector[i][0],vector[i][1],'bo')
line_x = [0,10]
line_y = [0,0]
for j in range(len(line_x)):
line_y[j] = (-weight[0]*line_x[j]-bias)/weight[1]
plt.plot(line_x,line_y)
plt.savefig("picture.jpg")
if __name__=="__main__":
x1 = np.array([[3, 3], [4, 3], [1, 1]])
x2 = np.array([1, 1, -1])
perception = Perceptron()
weight,bias = perception.interation(x1,x2)
perception.plot_graph(x1,x2,weight,bias)
运行结果展示
2.2.2 对偶形式Python代码
# -*- coding: utf-8 -*-
# 感知机的对偶形式
import numpy as np
import matplotlib.pyplot as plt
class Perceptron_dual(object):
def __init__(self,eta=1,iter=50):
# eta:学习率;itea:最大迭代次数
self.eta = eta
self.iter = iter
# 迭代修正权值和偏置
def interation(self,vector,label):
"""
input:
param vector: 训练数据向量
param label: 训练数据的原始划分类别
return:
alpha,weight and bias: 学习到的参数、权值与偏置
"""
data_shape = vector.shape
print("data_shape", data_shape)
# 初始化权值参数为零向量
self.alpha = np.zeros(data_shape[0])
# 初始化权值为零向量
self.weight = np.zeros(data_shape[1])
# 初始偏置
self.bias = 0
# 计算Gram矩阵
gram = np.matmul(vector,vector.T)
# 记录每一轮迭代还没有误分类数据
errors_point = 0
# True表示还需要继续迭代
check_inter = True
n = 0
print("迭代次数:%d , 初始参数:%s , 初始偏置:%s" % (n,str(self.alpha), str(self.bias)))
while check_inter and n<self.iter:
n +=1
errors_point = 0
for i in range(len(vector)):
xi = vector[i]
yi = label[i]
tmp = 0
for j in range(len(vector)):
tmp +=self.alpha[j]*label[j]*gram[i,j]
tmp +=self.bias
# 感知机中的判断数据有没有被误分类的公式
if yi*tmp <= 0:
# 修正权值和偏置
self.alpha[i] = self.alpha[i]+self.eta
self.bias = self.bias+self.eta*yi
errors_point +=1
print("迭代次数:%d , 误分类点:%s , 参数:%s , 偏置:%s" % (n,str(xi),str(self.alpha),str(self.bias)))
break
if errors_point:
# 如果还存在误分类点,则继续迭代
check_inter = True
else:
# 如果不存误分类点,则停止迭代
check_inter = False
for i in range(len(vector)):
self.weight +=self.alpha[i]*vector[i]*label[i]
print("权值:%s , 偏置:%s" % (str(self.weight),str(self.bias)))
return self.alpha,self.weight,self.bias
def plot_graph(self,vector,label,weight,bias):
plt.figure()
for i in range(len(vector)):
if label[i] == 1:
plt.plot(vector[i][0],vector[i][1],'ro')
else:
plt.plot(vector[i][0],vector[i][1],'bo')
line_x = [0,10]
line_y = [0,0]
for j in range(len(line_x)):
line_y[j] = (-weight[0]*line_x[j]-bias)/weight[1]
plt.plot(line_x,line_y)
plt.savefig("picture.jpg")
if __name__=="__main__":
x1 = np.array([[3, 3], [4, 3], [1, 1]])
x2 = np.array([1, 1, -1])
perception = Perceptron_dual()
alpha,weight,bias = perception.interation(x1,x2)
perception.plot_graph(x1,x2,weight,bias)
运行结果展示
最终的分类结果展示
在文章的最后,我想说明,代码中的感知机模块,不仅仅限于二维特征向量,不过画图函数可能无法实现更高维了.
参考博客:
https://blog.csdn.net/Big_Pai/article/details/88740612
https://blog.csdn.net/W_peijian/article/details/79098649
https://blog.csdn.net/winter_evening/article/details/70196040