一.感知机的主要思想
1.1感知机定义
感知机是二分类的线性模型,其输入为实例的特征向量,输出为实例的类别,取+1或-1.
通过大量训练集来训练模型,最后得出最佳分离超平面,使所有样本点能够正确分类。
1.2 感知机原理
在对应输入空间(特征空间)中,将实例线性划分为正负两类的分离超平面,属于判别模型;若不是线性划分的数据集,则无法获得超平面。
数据集的线性可分
在给定数据集
其中 能够将数据集的正实例和负实例完全正确地划分到超平面的两侧,即对所有 yi=1的xi,有w*xi+b>0;对所有yi=-1的xi,有w*xi+b<=0,则称该数据集线性可分,否则线性不可分。
那什么是超平面呢?
超平面是在空间Rn中的一个子空间Rn−1;在2维空间中的超平面是一条线,在3维空间中的超平面是一个平面。
在二维空间中,超平面 将特征空间划分为两部分,位于其中的点被分为正、负两类,这样的超平面 称为分离超平面。
分离超平面:

数学中求点到线的距离
公式中的直线方程为Ax+By+C=0,点P的坐标为(x0,y0)。

样本到超平面距离
我们假设超平面是h=w⋅x+b,其中w=(w0,w1,...wm),x=(x0,x1,...xm),样本点x′到超平面的距离如下:

二.感知机的过程
假设输入空间(特征空间)是X∈Rn,输出空间是y={+1,-1}其中,X表示实例的特征向量,对应输入特征空间的点;输出y表示实例的类别。
感知机学习的目标是求得一个能够将训练集正实数例点和负实例点完全正确线性分开的分离超平面。为了找出这样的超平面,即确定感知模型参数W,b。
三.数学模型
感知机从输入空间到输出空间的模型如下:
四.感知机的学习策略
由样本点到超平面的距离公式可知:当输入空间Rn中任意一点x到超平面的距离如下
其中 ||w||是w 的 范数(即根号系数的平方和)
由于感知机的目的是逐渐训练模型,使误分类点归为正确分类的位置,最后确定系数w,b得出最佳模型;那怎么判断所有的样本点已经归为正确分类呢?下面是两种方法:
自然选择:可以通过误分类点的数目判断,但是损失函数不是w,b 连续可导,不宜优化(舍去)
另一选择:求所有的误分类点到超平面的总距离,当所有误分类点的总距离变为0时,就判断不存在误分类了。
对于所有误分类的数据点(xi,yi)来说都有:
-yi(w.xi+b)>0
因为当(w.xi+b)>0时,本来正确的点应该yi>0,但是误分类点的yi<0,所以-yi(w.xi+b)>0
当w.xi+b)<0时,本来正确的点应该yi<0,但是误分类点的yi>0,所以-yi(w.xi+b)>0
因此求误分类点x0(xi,yi)到超平面的距离:
去绝对值,引入-yi,因为yi只能+1,-1,且 -yi(w.xi+b)>0,得到:
最后所有误分点集合到超平面的总距离为:(其中m为所有的误分类点的集合)
不考虑1/(||w||),就是感知机的损失函数
五.感知机的学习算法
输入:训练数据集T=(x1,y1),(x2,y2),...,(xN,yN),yi∈{−1,+1},学习率η(0<η<1)
输出:w,b;感知机模型f(x)=sign(w.x+b)
所有误分点集合到超平面的总距离为s:(其中m为所有的误分类点的集合)
不考虑,就是感知机的损失函数
显然损失函数为非负数,如果没有误分类点,则损失函数是0;误分类点越少,损失函数数值越小。
感知机学习策略:选取使损失函数最小的模型参数w,b ,即为感知机模型
至于为什么不考虑,可能有以下原因:
①不影响-yi(w⋅xi+b)正负的判断,即不影响学习算法的中间过程。因为感知机学习算法是误分类驱动的,这里需要注意的是所谓的“误分类驱动”指的是我们只需要判断 −yi(w⋅xi+b)的正负来判断分类的正确与否,而并不影响正负值的判断; 因为误分类点始终要−yi(w⋅xi+b)>0,正确分类点−yi(w⋅xi+b)<0所以
对感知机学习算法的中间过程可以不考虑。
②不影响感知机学习算法的最终结果。因为感知机学习算法最终的终止条件是所有的输入都被正确分类,即不存在误分类的点,所有误分点集合到超平面的总距离为0,即此时损
失函数为0. 对应于的分子为0.则可以看出
对最终结果也无影响。
感知机学习算法
是对上述损失函数进行极小化,得w和b。
但是用普通的基于所有样本的梯度和的均值的批量梯度下降法(BGD)是行不通的,原因在于我们的损失函数里面有限定,只有误分类的M集合里面的样本才能参与损失函数的优化。所以我们不能用最普通的批量梯度下降,只能采用随机梯度下降(SGD)。
目标函数:

原始形式算法
输入:训练数据集T=(x1,y1),(x2,y2),...,(xN,yN),yi∈{−1,+1},学习率η(0<η<1)
输出:w,b;感知机模型f(x)=sign(w⋅x+b)
- 赋初值 w0,b0
- 选取数据点(xi,yi)
- 判断该数据点是否为当前模型的误分类点,即判断若yi(w⋅xi+b)<=0为误分类点则更新
4. 转至(2),直至没有误分类点为止
直观解释:当一个实例点被误分类,即位于分离超平面的错误一侧,则调整 w,b的值,使分离超平面向该误分类点的一侧移动,以减少该误分类点与超平面的距离,直至超平面越过该分类点使其被分类正确。
假设误分类点集合m是固定的,那么对损失函数L(w,b)的梯度下降:
六.感知机原始形式实现(python)
完整代码github:https://github.com/xucancan1617608769/machine-learning.git
主要代码如下:(缺少部分plot文件代码)
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_iris
from plot import plot_decision_regions
class perception(object):
def __init__(self,alpha=0.01,loop=50): #初始化参数
self.alpha=alpha
self.loop=loop
def train(self,x,y): #训练模型,x为m×n的矩阵,是多个数据集,y是m×1数据集标签
self.w=np.zeros(x.shape[1]) #初始化w,b
self.b=0
self.erros=[]
err=0
for _ in range(self.loop):
for xi,yi in zip(x,y): #把它变成[(x1,y1),(x2,y2),..]的形式
self.w=self.w+self.alpha*(yi-self.predict(xi))*xi #用了梯度下降算法
upd=yi-self.predict(xi)
self.b=self.b+self.alpha*upd
err+=int(upd) #统计更新和,以便知道什么时候w,b趋向于稳定状态
if(err==0):
break
self.erros.append(err)
return self
def predict(self,x): #预测值
t=np.dot(x,self.w)+self.b #对每一个数据进行w*x+b线性转化
return np.where(t>0.0,1,-1) #预测值
def main():
iris = load_iris()
X = iris.data[:100, [0, 2]] #提取100个带有两个特征值的数据集
y = iris.target[:100] #提取100个标签
#print(y)
y = np.where(y == 1, 1, -1) #构建正规标签
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3) #用来随机划分样本数据为训练集和测试集的
ppn = perception(alpha=0.1, loop=10)
ppn.train(X_train, y_train) #训练集训练模型
plot_decision_regions(ppn, X, y)#打印模型
main()
运行结果:
七.对偶形式算法
对偶形式的基本想法是:将w和b表示为实例xi和标签yi的线性组合的形式,通过求解其系数而求得w和b.
原始形式的w,b梯度更新公式为:
w=w+ηyixi
b=b+ηyi
初始w,b均为0,我们的w,b经过了n次修改后的,参数可以变化为下公式,其中αi=ni*μ:
为了减少计算量,我们可以预先计算式中的内积,得到Gram矩阵
G=[xi,xj]N×N
八.对偶形式算法的实现
完整代码github:https://github.com/xucancan1617608769/machine-learning.git
主要代码如下:(缺少部分plot文件代码)
from sklearn.datasets import load_iris
import numpy as np
from sklearn.model_selection import train_test_split
from plot import plot_decision_regions
class perception(object):
def __init__(self,eta=0.1,loop=100):
self.eta=eta
self.loop=loop
#构建gram矩阵
def gram(self,X_train):
n_sample= X_train.shape[0] #样本的个数
self.gramer=np.zeros((n_sample,n_sample)) #初始化gram矩阵
for m in range(n_sample):
for n in range(n_sample):
self.gramer[m][n]=np.dot(X_train[m],X_train[n]) #内积矩阵
def panduan(self,x,y,i): #判断是否是误分类点,遍历其中一个样本点看是否是误分类点
temp=self.b
n_sample=x.shape[0]
for m in range(n_sample): #遍历其中一个样本点,检查是否是误分类点
temp+=self.alpha[m]*y[m]*self.gramer[i][m]
return y[i]*temp
def fit(self,x_train,y_train): #训练模型
i=0
x_sample=x_train.shape[0]
self.alpha=[0]*x_sample #初始化alpha
self.w = np.zeros(x_train.shape[1]) #初始化w
self.b=0 #初始化b
self.gram(x_train) #构建gram矩阵
while(i<x_sample): #遍历所有的样本点
if self.panduan(x_train,y_train,i)<=0: #判断每个点是否是误分类点
self.alpha[i]+=self.eta #如果是,更新alpha[i]
self.b+=y_train[i]*self.eta #更新 b
i=0 #说明还有误分类点,继续检查
else:
i+=1
for j in range(self.loop): #更新w
self.w+=self.alpha[j]*x_train[j]*y_train[j]
return self
def predict(self,x): #测试data,预测值target
t=np.dot(x,self.w)+self.b
return(np.where(t>0.0,1,-1))
def main():
load=load_iris()
x=load.data[:100,:2]
y=load.target[:100]
y=np.where(y==1,1,-1)
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.3)
clf=perception(eta=0.1,loop=30)
clf.fit(x_train,y_train) #训练模型
plot_decision_regions(clf, x, y)
if __name__ == '__main__':
main()
运行结果: