機器學習線性分類演算法:感知器原理
感知器PLA是一種最簡單,最基本的線性分類演算法(二分類)。其前提是資料本身是線性可分的。
模型可以定義為
,sign函式是階躍函式,閾值決定取0或1。
模型選擇的策略,利用經驗損失函式衡量演算法效能,由於該演算法最後得到一個分離超平面,所以損失函式可以定義為
,由於對於誤分類點,yi和wx+b的正負屬性相反,所以,所以加一個符號,來表徵樣例點與超平面的距離(此處不利用誤分類點的個數的原因,由於不可導,不易優化)。
演算法選擇,最終的目標是求損失函式的最小值,利用機器學習中最常用的梯度下降GD或者隨機梯度下降SGD來求解(相關優化演算法的理解請自行百度)。
SGD演算法的流程如下:輸入訓練集和學習率
1、初始化w0,b0,確定初始化超平面,並確定各樣例點是否正確分類(利用yi和wx+b的正負性關係);
2、隨機在誤分類點中選擇一個樣例點,計算L關於w和b在該點處的梯度值;
3、更新w,b,按照如下方向
;
4、迭代執行,直到滿足停止條件(限定迭代次數或者定義可接受誤差最大值);
可知,初值的選擇,誤分類點的選擇順序都影響演算法的效能和執行時間。PLA是一個很基本的演算法,應用場景很受限,只是作為一個引子來了解機器學習,後面有很多高階的演算法,比如SVM和MLP,以及大熱的deep learning,都是感知器的擴充套件。
此外,根據novikoff定理,可得到一些結論:1、存在|w|=1的超平面可以將案例分開,且
;2、
表示誤分類次數;此處w和x是擴充套件後的樣例資料,將權值和1分別連線到原來的w和b後形成一個大向量。
對於PLA,還有一個對偶問題,此處,簡單介紹一下對偶問題相關的知識。
對偶問題:
每一個線性規劃問題,我們稱之為原始問題,都有一個與之對應的線性規劃問題我們稱之為對偶問題。原始問題與對偶問題的解是對應的,得出一個問題的解,另一個問題的解也就得到了。並且原始問題與對偶問題在形式上存在很簡單的對應關係:目標函式對原始問題是極大化,對對偶問題則是極小化
原始問題目標函式中的收益係數(優化函式中變數前面的係數)是對偶問題約束不等式中的右端常數,而原始問題約束不等式中的右端常數則是對偶問題中目標函式的收益係數
原始問題和對偶問題的約束不等式的符號方向相反
原始問題約束不等式係數矩陣轉置後即為對偶問題的約束不等式的係數矩陣 原始問題的約束方程數對應於對偶問題的變數數,而原始問題的變數數對應於對偶問題的約束方程數
對偶問題的對偶問題是原始問題 總之他們存在著簡單的矩陣轉置,係數變換的關係。當問題通過對偶變換後經常會呈現許多便利,如約束條件變少、優化變數變少,使得問題的求解證明更加方便計算可能更加方便。
對偶問題中,此處將w和b看成是x和y的函式,w和b可表示為
,ni表示更新次數,模型
,演算法流程如下:輸入訓練集,學習率
1、
;
2、隨機選取誤分類點對,並更新計算
,具體更新,依據上面的表示式;
3、直至沒有誤分類點,停止計算,返回相應的引數;
原始問題和對偶問題都是嚴格可收斂的,線上性可分的條件下,一定可以停止演算法執行,會達到結果,存在多個解。
如果線性不可分,可以利用口袋演算法,每次迭代更新錯誤最小的權值,且規定迭代次數。口袋演算法基於貪心的思想。他總是讓遇到的最好的線拿在自己的手上。。。 就是我首先手裡有一條分割線wt,發現他在資料點(xn,yn)上面犯了錯誤,那我們就糾正這個分割線得到wt+1,我們然後讓wt與wt+1遍歷所有的資料,看哪條線犯的錯誤少。如果wt+1犯的錯誤少,那麼就讓wt+1替代wt,否則wt不變。 那怎樣讓演算法停下來呢??——–我們就 自己規定迭代的次數 由於口袋演算法得到的線越來越好(PLA就不一定了,PLA是最終結果最好,其他情況就說不準了),所以我們就 自己規定迭代的次數 。
以下是PLA簡單的程式碼實現
#include<iostream>
#include <stdio.h>
using namespace std;
double hypothose(double w[],int feature_num,double* training_set){
double sum=0;
for(int i=0;i<feature_num;i++){
sum+=w[i]*training_set[i];
}
if (sum>0) return 1;
else return 0;
}
//以下函式為感知器演算法真正函式,引數分別是特徵個數,訓練樣本數,學習速率,迭代次數,訓練樣本,初始w陣列
void perception(int feature_num,int training_num,double a,int times,double** training_set,double w[]){
int dimentions=feature_num+1;
while(times--){
double* delta_w=new double[feature_num];
for(int i=0;i<feature_num;i++){
delta_w[i]=0;
}
for(int i=0;i<training_num;i++){
for(int j=0;j<feature_num;j++){
delta_w[j]+=(training_set[i][feature_num]-hypothose(w,feature_num,training_set[i]))*training_set[i][j]*a;
}
}
for(int i=0;i<feature_num;i++){
w[i]+=delta_w[i];
}
delete[] delta_w;
}
}
int main(){
int feature_num,training_num,times;
double a;
freopen("in.txt","r",stdin);
while(cin>>feature_num>>training_num>>a>>times){
double** training_set=new double*[ training_num];
for(int i=0;i<training_num;i++){
training_set[i]=new double[training_num+2];
}
double* w=new double[feature_num+1];
for(int i=0;i<training_num;i++){
training_set[i][0]=1;
}
for(int i=0;i<training_num;i++){
for(int j=1;j<=feature_num+1;j++){
cin>>training_set[i][j];
}
}
for(int i=0;i<=feature_num;i++){
cin>>w[i];
}
perception(feature_num+1,training_num,a,times,training_set,w);
for(int i=0;i<feature_num;i++){
cout<<w[i]<<' ';
}
cout<<w[feature_num]<<endl;
delete[] w;
for(int i=0;i<training_num;i++){
delete[] training_set[i];
}
delete[] training_set;
}
return 0;
}
至此,第一個具體的演算法PLA就已經總結完畢。