本节的实例将使用感知机模型,对鸢尾花进行分类,并调整参数,对比分类效率。
感知机(perceptron)是二类分类的线性分类模型。
· 输入:实例的特征向量。
· 输出:实例的类别,取值为+1和-1。
· 感知机对应于输入空间(特征空间)中将实例划分为正、负两类的分离超平面,属于判别模型。
· 旨在求出将训练数据进行线性划分的分离超平面,为此,导入基于误分类的损失函数,利用梯度下降法对损失函数进行极小化,求得感知机模型。
· 感知机学习算法具有简单而易于实现的优点,分为原始形式和对偶形式。
· 预测:对新的输入进行分类。
【例2-1】 在鸢尾花数据集上训练感知机模型。
具体的实现步骤如下。
(1)数据处理。
数据采用sklearn内置的鸢尾花数据。
import numpy as np import pandas as pd from sklearn.datasets import load_iris #读取鸢尾花数据 iris=load_iris() #将鸢尾花4个特征,以4列存入Pandas的数据框架 df=pd.DataFrame(iris.data,columns=iris.feature_names) #在最后一列加入标签(分类)列数据 df['lab']= iris.target #选取前两种花进行划分(每种数据50组) plt.scatter(df[:50][iris.feature_names[0]],df[:50][iris.feature_names[1]],label=iris .target_names[0]) plt.scatter(df[50:100][iris.feature_names[0]],df[50:100][iris.feature_names[1]],label= iris.target_names[1]) plt.xlabel(iris.feature_names[0]) plt.ylabel(iris.feature_names[1]) plt.xlabel(u"花瓣长度") plt.ylabel(u"萼片长度") #选取数据前100行,前两个为特征,最后一列为标签 data=np.array(df.iloc[:100,[0,1,-1]]) #X是除最后一列外的所有列,y是最后一列 X,y=data[:,:-1],data[:,-1] #生成感知机的标签值:+1,-1,第一种是-1,第二种是+1 y=np.array([1 if i==1 else-1 for i in y])
运行程序,效果如图2-4所示。
(2)编写感知机类。
class Perceptron Model(): def__init__(self,X,y,eta): self.w=np.zeros(len(X[0]),dtype=np.float) #权重 self.b=0 #偏置 self.eta=eta #学习率 self.dataX=X #数据 self.datay=y #标签
图2-4 鸢尾花散点图
self.iterTimes=0 #迭代次数 #对偶形式的参数 self.a=np.zeros(len(X),dtype=np.float) #alpha self.Gmatrix=np.zeros((len(X),len(X)),dtype=np.float) self.calculateGmatrix() #计算Gram矩阵 def sign0(self,x,w,b): #原始形式sign函数 y=np.dot(w,x)+b return y def sign1(self,a,G_j,Y,b): #对偶形式sign函数 y=np.dot(np.multiply(a,Y),G_j)+b return y def OriginClassifier(self): #原始形式的分类算法 self.iterTimes=0 self.b=0 stop=False while not stop: wrong_count=0 for i in range(len(self.dataX)): X=self.dataX[i] y=self.datay[i] if(y * self.sign0(X,self.w,self.b))<=0: self.w+=self.eta * np.dot(X,y) self.b+=self.eta * y wrong_count+=1 self.iterTimes+=1 if wrong_count==0: stop=True print("原始形式,分类完成!步长:%.4f,共迭代%d次"%(self.eta,self.iterTimes)) def calculateGmatrix(self): #计算Gram矩阵 for i in range(len(self.dataX)): for j in range(0,i+1): self.Gmatrix[i][j]=np.dot(self.dataX[i],self.dataX[j]) self.Gmatrix[j][i]=self.Gmatrix[i][j] def DualFormClassifier(self): #对偶形式分类算法 self.iterTimes=0 self.b=0 stop=False while not stop: wrong_count=0 for i in range(len(self.dataX)): y= self.datay[i] G_i=self.Gmatrix[i] if(y * self.sign1(self.a,G_i,self.datay,self.b))<=0: self.a[i]+=self.eta self.b+=self.eta * y wrong_count+=1 self.iterTimes+=1 if wrong_count==0: stop=True print("对偶形式,分类完成!步长:%.4f,共迭代%d次"%(self.eta,self.iterTimes))
运行程序,输出如下:
原始形式,分类完成!步长:0.3000,共迭代1518次 对偶形式,分类完成!步长:0.3000,共迭代1562次
(3)多参数组合运行。
#调用感知机进行分类,eta为学习率 perceptron=Perceptron Model(X,y,eta=0.3) perceptron.OriginClassifier() #原始形式分类 #绘制原始算法分类超平面 x_points=np.linspace(4,7,10) y0=-(perceptron.w[0] * x_points+perceptron.b)/perceptron.w[1] plt.plot(x_points,y0,'r',label='原始算法分类线') perceptron.DualFormClassifier() #对偶形式分类 #由alpha,b计算omega向量 omega0= sum(perceptron.a[i] * y[i] * X[i][0]for i in range(len(X))) omega1= sum(perceptron.a[i] * y[i] * X[i][1]for i in range(len(X))) y1=-(omega0 * x_points+perceptron.b)/omega1 #绘制对偶算法分类超平面 plt.plot(x_points,y1,'b',label='对偶算法分类线') plt.rcParams['font.sans-serif']= 'SimHei' #消除中文乱码 plt.legend() plt.show()
运行程序,效果如图2-5所示。
图2-5 感知机分类效果
当学习率不同时,可查看不同的迭代过程,代码如下:
#学习率不同,查看迭代次数 n=100 i=0 eta_iterTime=np.zeros((n,3),dtype=float) for eta in np.linspace(0.01,1.01,n): eta_iterTime[i][0]=eta #第一列,学习率 perceptron=Perceptron Model(X,y,eta) perceptron.OriginClassifier() eta_iterTime[i][1]=perceptron.iterTimes #第二列,原始算法迭代次数 perceptron.DualFormClassifier() eta_iterTime[i][2]=perceptron.iterTimes #第三列,对偶算法迭代次数 i+=1 x=eta_iterTime[:,0] #数据切片 y0=eta_iterTime[:,1] y1=eta_iterTime[:,2] plt.scatter(x,y0,c='r',marker='o',label='原始算法') plt.scatter(x,y1,c='b',marker='x',label='对偶算法') plt.xlabel('步长(学习率)') plt.ylabel('迭代次数') plt.title("不同步长,不同算法形式下,迭代次数") plt.legend() plt.show()
运行程序,迭代过程如下,效果如图2-6所示。
原始形式,分类完成!步长:0.0100,共迭代1518次 对偶形式,分类完成!步长:0.0100,共迭代1518次 原始形式,分类完成!步长:0.0201,共迭代1562次 ... 对偶形式,分类完成!步长:0.9999,共迭代1518次 原始形式,分类完成!步长:1.0100,共迭代1562次 对偶形式,分类完成!步长:1.0100,共迭代1518次
图2-6 迭代过程效果
由以上结果可得出:
· 感知机的两种算法形式均会因为初值和学习率的不同而造成多种迭代路径。
· 对于线性可分的数据,感知机学习算法迭代是收敛的。
如果利用感知机sklearn包实现,其相应的实现代码相对简单些:
classify=Perceptron(fit_intercept=True,max_iter=10000,shuffle=False,eta0=0.5,tol=None) classify.fit(X,y) print("特征权重:",classify.coef_) #特征权重为w print("截距(偏置):",classify.intercept_) #截距为b #可视化 plt.scatter(df[:50][iris.feature_names[0]],df[:50][iris.feature_names[1]],label=iris .target_names[0]) plt.scatter(df[50:100][iris.feature_names[0]],df[50:100][iris.feature_names[1]],label= iris.target_names[1]) plt.xlabel(iris.feature_names[0]) plt.ylabel(iris.feature_names[1]) #绘制分类超平面 x_points=np.linspace(4,7,10) y=-(classify.coef_[0][0] * x_points+classify.intercept_)/classify.coef_[0][1] plt.plot(x_points,y,'r',label='sklearn Perceptron分类线') plt.title("sklearn内置感知机分类") plt.legend() plt.show()
运行程序,输出如下,效果如图2-7所示。
特征权重:[[39.9-50.7]] 截距(偏置):[-63.]
图2-7 sklearn内置感知机分类效果