**前言:**最近看了李航的《统计学习方法》,有了一点收获,所以打算写一个系列的笔记,一是为了巩固知识点,二是加深自己对课本内容的理解。
—————————————————————————————————————
一、感知机是什么?
感知机(preceptron)是二类分类的线性分类模型,其输入为实例的特征向量,输出为实例的类别,去+1和-1二值。
二类分类,怎么理解呢?我们知道统计学习方法(又称机器学习)按统计学习的方法可以分为监督学习,无监督学习,强化学习等。监督学习有一些应用场景,常见如分类问题、标注问题,回归问题等。感知机是监督学习的一种,它主要解决的是分类问题,并且是分类问题中一种特殊的分类问题,因为它的类别只有两类。
线性分类,指的是假设空间(决策函数的集合)中的元素(函数)是线性(函数)的。如下
f
(
x
)
=
s
i
g
n
(
ω
⋅
x
+
b
)
f(x)=sign (\omega \cdot x+b)
f(x)=sign(ω⋅x+b)
模型,感知机不是一个很神秘、高大上的东西,它实际上就是一个(判别)模型,它所要解决的问题是判别某个实例(输入)的类别(输出)。它所解决问题是通过
s
i
g
n
(
x
)
sign (x)
sign(x)函数来实现的,所以它实际上一个满足某些要求的函数罢了。
二、什么是感知机?
上面解释了感知机到底是一个什么东西(用途),接下来需要解释什么是感知机(定义)。
假设输入空间(特征空间)是 X ⊆ R n X \subseteq\mathbb{R}^n X⊆Rn,输出空间是 Y = { − 1 , 1 } Y=\{-1,1\} Y={−1,1}.输入 x ∈ X x\in X x∈X表示实例的特征向量,对应与输入空间(特征空间)的点;输出 y ∈ Y y\in Y y∈Y表示实例的类别,由输入空间到输出空间的如下函数:
f ( x ) = s i g n ( ω ⋅ x + b ) f(x)=sign (\omega \cdot x+b) f(x)=sign(ω⋅x+b)
称为感知机。其中 ω \omega ω和 b b b为感知机的模型参数, ω ∈ R n \omega\in\mathbb{R}^n ω∈Rn叫做权值或权值向量, b ∈ R n b\in\mathbb{R}^n b∈Rn叫做偏置, w ⋅ x w\cdot x w⋅x表示 ω \omega ω和 x x x的内积,设 ω = ( ω 1 , ω 2 , ⋯ , ω n ) \omega=(\omega_1,\omega_2,\cdots,\omega_n) ω=(ω1,ω2,⋯,ωn), x = ( x 1 , x 2 , ⋯ , x n ) x=(x_1,x_2,\cdots,x_n) x=(x1,x2,⋯,xn)则
w ⋅ x = ∑ i = 1 n ω i x i w\cdot x=\sum\limits_{i=1}^{n}\omega_ix_i w⋅x=i=1∑nωixi
s i g n sign sign是符号函数,即
s i g n ( x ) = { + 1 , x ≥ 0 − 1 , x < 0 sign (x)=\left\{ \begin{array}{l} +1,~x\ge0\\ -1,~x<0 \end{array} \right. sign(x)={+1, x≥0−1, x<0
**
三、感知机的实现原理
感知机的几何解释:根据它的定义我们知道感知机实际上表示一个超平面(指的是符号函数里的线性函数,因为变量 x x x是 n n n维的,如果是三维的就是一个平面,二维就是一条直线)。我们的目标是要从所有的超平面中找到一个特殊的超平面,怎么个特殊法?一个超平面将一个空间划分成两部分,在同一部分中的点属于同一类(也就是y是相同的),不好理解的话可以通过二维平面来理解,这就有点像我们初中学的解不等式,一条直线将平面划分成两部分,原理是一样的。
这里首先介绍一下数据集的线性可分性(具体定义这里就不再叙述了),它的作用是保证我们要寻找的超平面是存在的(进而保证我们后续的算法是收敛的)。简单举个例子,感知机现在就是要找到一个有女朋友的男生,给他买一件衣服,那么这是我们要在给定的男生(数据集)中找到那个幸运的男生,这个时候就需要给定的男生中至少有一个男生是有女朋友的,不然还选个锤子呀,这就是线性可分性。
学习策略
所谓学习策略,就是如何找到那个幸运的男生,不对,是如何找到那个超平面。给出如下的损失函数(推导过程就不再细说了)
L
(
ω
,
b
)
=
−
∑
x
i
∈
M
y
i
(
ω
⋅
x
i
+
b
)
L(\omega,b)=-\sum\limits_{x_i\in M}y_i(\omega\cdot x_i+b)
L(ω,b)=−xi∈M∑yi(ω⋅xi+b)
其中
M
M
M是误分类点的集合
我们的学习策略就是损失函数最小,也就是求解如下无约束最优化问题,根据梯度下降法(这里使用的随机梯度下降法,关于这两个算法大家可以自行去查阅)来更新感知机的模型参数。算法思路如下
- 选取初值 ω 0 \omega_0 ω0, b 0 b_0 b0;
- 在训练集中选取数据 ( x i , y i ) (x_i,y_i) (xi,yi),注意 x i ∈ R n x_i\in\mathbb{R^n} xi∈Rn是一个向量, y i ∈ { 1 , − 1 } y_i\in\{1,-1\} yi∈{1,−1}是一个实数
- 如果
y
i
(
ω
⋅
x
i
+
b
)
≤
0
y_i(\omega\cdot x_i+b)\le0
yi(ω⋅xi+b)≤0(判断更新的条件,即该点是不是分类错误了,当
ω
⋅
x
i
+
b
≥
0
\omega\cdot x_i+b\ge0
ω⋅xi+b≥0时,代入感知机模型将它分为
y
i
=
1
y_i=1
yi=1,但是它实际上
y
i
=
−
1
y_i=-1
yi=−1,所以分类错误):
ω ← ω + η y i x i \omega\leftarrow\omega+\eta y_ix_i ω←ω+ηyixi
b ← b + η y i b\leftarrow b+\eta y_i b←b+ηyi
(将右边的值赋值给左边) - 回到步骤2,直到训练集中没有误分类点。
上面就是感知机的具体算法思路(原始形式),还有一种对偶形式的算法这里就不多介绍了,本质上没有太大区别。另外如果训练集是线性可分的,则该算法是收敛的(证明过程不再说明),若不收敛,该算法就不收敛,迭代结果会发生震荡。
注: 该算法最后的结果会受到初值以及选取误分类的不同而不同,也就是说训练集中有很多男生都有女朋友,都符合我们要找的要求,不对,是有不只一个超平面符合要求,所以最后的结果是不唯一的。
四、代码实现(基于python)
个人感觉用Matlab可能更方便一些,但是笔者最近想练练python,所以就用python写了。
给定如下训练集:
{(3,3;1),(4,3;1),(1,1;-1)}
即找到一个超平面将正类中的点 ( 3 , 3 ) (3,3) (3,3), ( 4 , 3 ) (4,3) (4,3)以及负类中的点 ( 1 , 1 ) (1,1) (1,1)分开。
#数据的读取
import sys
import numpy as np
import random
re=[]#存储输入数据
for line in sys.stdin:
new_line=line.replace(","," ").split()
new_line=[int(x) for x in new_line]#将列表中的元素用str变成int型
re.append(new_line)
#数据的简单处理
re=np.array(re)#将它转换成N*(k+1)的二维数组
#感知机算法
N,k=re.shape
w=np.zeros((1,k-1))
b=2
eta=1#学习率
def L(point,w,b):
x=point[:-1]
y=point[-1]
z=np.dot(x,w.T)#计算内积
return y*(z+b)
def wrongpoint(data,w,b,N,k):
#找出所有的误分类点
re=[]
for i in range(N):
point=data[i]
if L(point,w,b)<=0:
re.append(point)
if re==[]:#表示已经没有未分类点了
val=False
point=np.empty((1,k))
return val,point
else:#还有未分类点
val=True
j=random.randint(0,len(re)-1)
point=re[j]#在未分类点的集合中随机选取一点
return val,point
flag,point=wrongpoint(re,w,b,N,k)
while flag:
if L(point,w,b)<=0:
#更新模型参数
x=point[:-1]
y=point[-1]
w=w+eta*y*x
b=b+eta*y
flag,point=wrongpoint(re,w,b,N,k)
print("w=",w[0])
print("b=",b)
print("感知机模型为:%s*x1+%s*x2+(%s)"%(w[0,0],w[0,1],b))
**注:**上面的代码可能写的复杂了写,关于随机选取一个未分类点可以有很多种实现方法,这里仅仅是提供一种思路。
数据输入格式如下,每行代表一个点,中间用逗号分开,最后一位代表y的值
例子1
3,3,1
4,3,1
1,1,-1
结果如下所示(因为用到的random函数,所以最后运行结果可能不一样)
我们将数据可视化来观察我们所找到的超平面是否将其完美的分类了
显然是符合要求的,代码如下
#数据的读取
import sys
import numpy as np
import random
re=[]#存储输入数据
for line in sys.stdin:
new_line=line.replace(","," ").split()
new_line=[int(x) for x in new_line]#将列表中的元素用str变成int型
re.append(new_line)
#数据的简单处理
re=np.array(re)#将它转换成N*(k+1)的二维数组
N,k=re.shape
import matplotlib.pyplot as plt
#数据可视化后观察所得结果是否满足要求
plt.figure()
#画出所有数据点
for i in range(N):
point=re[i]
x=point[0]
y=point[1]
plt.annotate("(%s,%s)"%(x,y),xy=(x,y))
plt.scatter(x,y)
#画出超平面
x=np.linspace(0,10)
y=3-x
plt.plot(x,y)
plt.show()
例子2
输入
1,9,1
2,6,1
3,6,1
4,-9,-1
-5,-2,-1
由此可见我们编写的算法是有效的。