感知器算法由 Rosenblatt 提出,其主要功能是通过设计分类器来判别样本所属的类别;通过对训练样本集的学习,从而得到判别函数权值的解,产生线性可分的样本判别函数。该算法属于非参数算法,优点是不需要对各类样本的统计性质作任何假设,属于确定性方法。
感知器算法是非常好的二分类在线算法。该算法求取一个分离超平面,超平面由w∈R 参数化并用来预测。对于一个样本x,感知器算法通过计算y=(wx)预测样本的标签。最终的预测标签通过计算sign(y)来实现。算法仅在预测错误时修正权值w 。如果正确的标签是 y =1,那么权值修正为w +x;如果y =−1 ,权值变为w −x ,可以总结为 w←w +yx;需要注意的是在预测后,尽管算法不能保证修正后的预测准则会正确分类目前的样本,但在目前样本上的分离超平面的间隔会增加,即算法是保守的不是主动的。
算法流程图如下所示:
算法步骤:
2. 感知器算法的实现
我是用Java语言来编写的,实现的比较简单的两类,两特征的感知器算法。该算法的目的是为了计算权向量W。程序开始时输入样本数N,然后程序随机产生N个样本值。
根据感知器算法的过程,程序在进行了15次计算后得到了最终的权向量W=(10,-2,-12)。所以线性分类判别函数是10x-2y-12=0,化简得y=5x-6。为验证该函数是否能正确分类,故把所有的样本值点在XOY平面标出来,并画出y=5x-6的函数图象,结果如下所示:
从上图中可以看出,该判别函数很好地把两类数据分开了,得到的结果还是比较准确的。
源代码:
import java.io.IOException; import java.util.Scanner; public class Perceptron { public static void main(String[] args) throws IOException { int N,C=1,t=1;//N是训练样本数,C是迭代过程中的系数,t为迭代次数 int[] W=new int[]{-1,1,0};//权向量W,初始设为(-1,1,0) int[] temp=new int[3];//作为与上一次迭代的W做比较 System.out.println("输入样本数"); Scanner strin=new Scanner(System.in); N=strin.nextInt();//输入样本数,随后产生随机样本 int[][] X=new int[4][N];//样本值 genesample(X); //第一次迭代, System.arraycopy(W, 0, temp, 0, W.length);//数组的深度复制 for(int i=0;i<N;i++){ W=panduan(W,X,i,C); } System.out.println("第1次迭代后权向量W是:"); arrayout(W); //继续迭代 while(!arrayequal(temp,W)){//如果不相等继续迭代计算 System.arraycopy(W, 0, temp, 0, W.length);//数组的赋值用此方法有效 t++; for(int i=0;i<N;i++){ W=panduan(W,X,i,C); } System.out.println("第"+t+"次迭代后权向量W是:"); arrayout(W); } System.out.println("经过"+t+"次迭代后权向量W是:"); arrayout(W); } //产生随机样本,是一个4*n的矩阵,1,2表示该样本的特征值,3代表增1矩阵值,4代表该样本所属的分类 public static void genesample(int[][] X){ for(int i=0;i<2;i++){//产生样本特征值 for(int j=0;j<X[1].length;j++){ X[i][j]=(int)(Math.random()*10); } } for(int m=0;m<X[1].length;m++){//增1矩阵 X[2][m]=1; } for(int k=0;k<X[1].length;k++){//产生样本所属的类别1和2 String chars = "12"; X[3][k]=Integer.parseInt(String.valueOf(chars.charAt((int)(Math.random()*2)))); } System.out.println("产生的样本值如下所示"); for(int a=0;a<4;a++){//输出产生的样本 for(int b=0;b<X[1].length;b++){ System.out.print(X[a][b]+" "); } System.out.println(); } } //计算Wk*Xk的值,即第k+1个样本值X与W的乘积 public static int calculate(int[] W,int[][] X,int i){ int a=0; for(int j=0;j<W.length;j++){ a=W[j]*X[j][i]+a; } return a; } //判断Wk*Xk的值,并做出修正,"必须要返回数组类型" public static int[] panduan(int[] W,int[][] X,int k,int C){ if(calculate(W,X,k)<=0&&X[3][k]==1){//Xk属于第一类,且Wk*Xk<=0,修正Wk的值,并重新计算 for(int i=0;i<W.length;i++){ W[i]=W[i]+C*X[i][k]; } } if(calculate(W,X,k)>=0&&X[3][k]==2){//Xk属于第二类,且Wk*Xk>=0,修正Wk的值 for(int i=0;i<W.length;i++){ W[i]=W[i]-C*X[i][k]; } } return W; } //判断两个int数组是否相等 public static Boolean arrayequal(int[] W1,int[] W2){ Boolean flag=true; for(int i=0;i<W1.length;i++){ if(W1[i]!=W2[i]){ flag=false; break; } } return flag; } //输出W的值 public static void arrayout(int[] W){ for(int k=0;k<W.length;k++){ System.out.print(W[k]+" "); } System.out.println(); } }