感知机(perceptron)是一种二类分类的线性分类模型,其输入为特征向量,输出为实例的分类,取“-1”“+1”两值。
简单的来说:感知机便是一个超平面,将训练数据进行线性划分。借助训练出来的超平面,输入测试数据,便可以得到预测结果
话不多说:
原理解释走起!
准备:
假设输入空间(特征空间)是X∈R(n),输出空间是Y={-1,+1}.
x对应于X,表示实例的特征向量,对应于输入空间的点,输出y∈Y。有输入到输出,对应于函数f(x)=sign(w · x+b) (这里的w·x,表示内积,就是∑wi*xi)
w通常叫做权值(weight),b 叫做偏置(bias),sign是符号函数
即sign(x)={+1,x>=0; -1,x<0}
感知机是一种线性分类模型,属于判别模型。感知机模型的假设空间是一系列超平面的集合,即函数的集合{f|f(x)=w·x+b}。
为什么集合,不是一个超平面就够了吗?
解释一下:感知机由于选取测试例子的顺序不同,会导致产生不同的超平面,即所有超平面都可以单独进行判断。
感知机的几何解释:线性方程:w·x+b=0
key words:
- 数据的线性可分性:
如果存在某个超平面能够将训练数据集完全正确的划分到其两侧,则称该数据集为线性可分数据集(linearly separable data set) - 输入空间上一点x0到达超平面S的距离为:|w·x0+b|/||w||,这里||w||是w的L2范数,即||w|| = [w·w] = ∑ (wi*wi)
- loss——function: 对于一个误分类(xi,yi)的例子总满足-yi*(w·xi+b) > 0。因为
① w·xi+b>0,yi = -1 ② w·xi+b<0, yi = +1,因此误分类点到超平面的总距离是 -∑yi(w·xi+b)/||w||。 不必考虑||w||, 就得到了损失函数 L(w,b) = -∑yi(w·xi+b) ,容易看出,这是一个关于w,b的可连续求导函数,这也是为什么要选择它做损失函数的原因之一,后面的梯度下降,它将大显神威
关于Noviloff的证明:
欢迎走进Dr.cyr的知乎专栏
algorithm:
这个算法并没有太难理解的东西,主要是遍历和迭代的思想
重点讲参数(w,b)的更新:
还记得损失函数L(w,b) = -∑yi(w·xi+b) 吧,对于它,我们要分别去偏导,
▽w = -∑yixi, ▽b= -∑yi
为什么要求导?
这个和随机梯度下降的方法是相似的。求导的目的是为了不断减小损失函数的值,设置学习率,可以理解成调整函数,关于学习率,不能设置过大,过大会导致容易错过最优选项,过小则造成运算时间过久,且容易陷入局部最优。对于新手而言,这个学习率的调整,主要靠尝试。
原理讲的差不多了,接下来开始代码实现
首先是python初始化版本实现:
import numpy as np
import matplotlib.pyplot as plt
class MyPerceptron:
def __init__(self):
self.w = None
self.b = 0
self.l_rate = 1
def draw(self,x,y,w,b):
fig = plt.figure()
ax = fig.add_subplot(111)#从左往右读,把画布分割成1行1列,并绘制1幅图,
ax.set_title('perceptron_show')
plt.xlabel('x(1)')
plt.ylabel('x(2)')
#绘制散点图
for i in range(np.shape(x)[0]):
if y[i] == -1: #如果取反例,则以”-“画点
ax.scatter(x[i][0],x[i][1],c = 'r',marker = '_')
else:#如果取正例,则以”+“画点
ax.scatter(x[i][0],x[i][1],c = 'r',marker = '+')
#绘制超平面,算法中超平面:w·x+b=0,因此可由x1_new得到x2_new,注意这里只是二维的超平面的演示
x1_new = np.arange(0,10,1)
x2_new = (-b - w[0]*x1_new)/w[1]
plt.plot(x1_new,x2_new,"b-")
plt.show()
#核心算法代码来袭
def fit(self,X_train,y_train):
self.w = np.zeros(np.shape(X_train)[1])#shape[1]表示列数
times = 0
while times < np.shape(y_train)[0]:#shape[0]表示行数
x = X_train[times]
y = y_train[times]
if y*(np.dot(self.w,x)+self.b) <= 0:
self.w = self.w + self.l_rate * np.dot(y,x)
self.b = self.b + self.l_rate * y
times = 0
else:
times += 1
if __name__=="__main__":
X_train = [[3,3],[4,3],[1,1]]
Y_train = [1,1,-1]
perceptron = MyPerceptron()
perceptron.fit(X_train,Y_train)
print(perceptron.w)
print(perceptron.b)
perceptron.draw(X_train,Y_train,perceptron.w,perceptron.b)
下面还有sk_learn 实现
值得一提,关于perceptron的内部参数
- penalty是正则化项,可选“l1”或者“l2”,“elasticnet”, l1使得特征更稀疏,l2使得权重更均匀。(模型选择的典型方法是正则化,正则化是结构风险最小化策略的实现,是在经验风险上加一个罚项(正则化项))
- alpha是正则化系数,太小则没有约束效果,太大则约束过度,导致欠拟合*(正则化项一般是模型复杂度的单调递增函数,模型越复杂,正则化值越大)*
- eta是学习率,本次实验由于w,b初始为0,所以eta并不会对最终结果有任何影响,因为学习率总是在最后被消去
- max_iter 是最多迭代次数
- tol是终止条件,一般是loss达到一定范围启动,当它启动时,可停止迭代
from sklearn.linear_model import Perceptron
if __name__=="__main__":
X_train = [[3, 3], [4, 3], [1, 1]]
Y_train = [1,1,-1]
#perceptron内部参数的默认值如下所示
perceptron = Perceptron(penalty= None,alpha=0.001,eta0=1,max_iter = 5,tol=None)
#fit函数用于训练
perceptron.fit(X_train,Y_train)
#"coef_"指的是特征向量,"intercept_"指的是偏置
print("the wight :",perceptron.coef_,"\nbias:",perceptron.intercept_)
#测试模型的准确率score()
res = perceptron.score(X_train,Y_train)
#n_iter是指迭代次数
print("the n_iter: ",perceptron.n_iter_)
print("correct rate:{:.0%}".format(res))
写在最后:
感知机有两种算法,一种是原始算法,如上,一种是对偶算法
当训练数据集N过大时,选择原始算法,当维度比较高时,选择对偶算法,可减小运算开销。
关于sk_learn对于感知机模型的选择,应该是正则化项(alpha值)的设置,如果设置比较大,就会增大结构风险,导致选择比较简单的模型。感知机的模型复杂度和维度有关,维度大而且我们设置较小的alpha,就会选择对偶模型
后续如果有空,再来写一写对偶算法的实现(溜了溜了)