機器學習:感知器(perceptron)


感知器

 

感知器以一個實數值向量作為輸入,計算這些輸入的線性組合,然后如果結果大於某個閾值就輸出1 ,否則輸出-1 。

更精確地,如果輸入為x,那么感知器計算的輸出為:

其中每一個w i 是一個實數常量,或叫做權值(weight ),用來決定輸入xi 對感知器輸出的貢獻率。

請注意,常量(w0) 是一個閾值,它是為了使感知器輸出 1 ,輸入的加權和w1x1+w2x2+...+wnxn必須超過的閾值。

(此圖是從網上扒來的,上圖下方的的b對應於w0,1對應於x1,f的圖像類似如下)

 

為了簡化表示,我們假想有一個附加的常量輸入x0=1,那么我們就可以把上邊的不等式寫成:

                                                  

或以向量形式寫為。為了簡短起見,有時會把感知器函數寫成:

                             ,

其中:

                        

   學習一個感知器意味着選擇權w0,......wn的值。

1感知器的表達能力

我們可以把感知器看作n維實例空間(即點空間)中的超平面決策面。對於超平面一側的實例,感知器輸出1,對於另一側的實例輸出-1,如圖1所示。這個決策超平面方程是。當然,某些正反樣例集合不可能被任一超平面分割。那些可以被分割的成為線性可分(linearly separable)的樣例集合。

 

      圖 1

單獨的感知器可以用來表示很多布爾函數。

例如,假如用1(真)和-1(假)表示布爾值,那么使用一個有兩輸入的感知器來實現與函數(AND)的一種方法就是設置w0=-0.8,w1=w2=0.5。

x1 x2 x3 輸出
1  1  1  1
1  1 -1 -1
1 -1  1 -1
1 -1 -1

-1

 

 

 

 

同樣的,這個感知器也可以來表示或函數(OR),那么只要改變它的權值w0=-0.3.

感知器可以表示所有的原子布爾函數(primitive  boolean  function):與,或,與非和或非。然而遺憾的是,一些布爾函數無法用單一的感知器表示,例如異或函數(XOR),它當且僅當x1≠ x2,時輸出為1.

圖2中線性不可分的訓練樣例集對應於異或函數。

    圖2

 

感知器表示與、或、與非、或非的能力是很重要的,因為所有的布爾函數都可表示為基於這些原子函數的互連單元的某個網絡。

事實上,僅用兩層深度的感知器網絡就可以表示所有的布爾函數,在這些網絡中輸入被送到多個單元,這些單元的輸出被輸入到第二級,也是最后一級。

因為閾值單元的網絡可以表示大量的函數,而單獨的單元不能做到這一點,所以通常我們感興趣的是學習閾值單元組成的多層網絡。

 

2感知器訓練法則

現在我們來解決如何學習單個感知器的權值,也就是決定一個權向量,使得感知器對於給定的訓練樣例輸出正確的1或-1.

 

為得到可接受的權向量,一種辦法是從隨機的權值開始,然后反復地應用這個感知器到每個訓練樣例,只要它誤分類樣例就修改感知器的權值。重復這個過程,知道感知器能正確分類所有的訓練樣例。

每一步根據感知器訓練法則(perceptron training rule)來修改權值,也就是修改與輸入 x對應的權 w法則如下:

                        

其中:

                      

這里t是當前訓練樣例的目標輸出,O是感知器的輸出(1或-1),η是一個正的常數稱為學習速率(learning rate)學習速率的作用是緩和每一步調整權的程度。它通常被設為一個小的數值(例如0.1),而且有時會使其隨着權調整次數的增加而衰減。

對於權值的調整是一例一調,也就是輸入一個樣例,就計算每個Δwi, 來調整wi的值,一直訓練到會收斂到一個能一個能正確分類所有訓練樣例的權向量,前提是訓練樣例線性可分,並且使用了充分小的η 。如果數據不是線性可分的,那么不能保證收斂。

 

實驗:

利用感知器法則來訓練訓練感知器能夠正確的表示與函數(AND)

訓練樣本:

1 1 1
1 -1 -1
-1 1 -1
-1 -1 -1

 

頭文件

      

#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <ctime> using namespace std; const int DataRow=4; const int DataColumn=3; const double learning_rate=.1; //學習速率 extern double DataTable[DataRow][DataColumn+1]; //訓練樣例 extern double Theta[DataColumn]; // 權值 const double loss_theta=0.001; // 誤差閾值 const int iterator_n =1000; //迭代次數 #endif // HEAD_H_INCLUDED

源代碼:

#include "head.h"

double DataTable[DataRow][DataColumn+1];
double Theta[DataColumn];

void Init()
{
    srand((unsigned)time(NULL));
    ifstream fin("data.txt");
    for(int i=0;i<DataRow;i++)
    {
        DataTable[i][0]=1;      // x1 默認為 1
        for(int j=1;j<DataColumn+1;j++)
        {
            fin>>DataTable[i][j];
        }
    }
    if(!fin)
    {
        cout<<"fin error";
        exit(1);
    }
    fin.close();
    for(int i=0;i<DataColumn;i++)
    {
        Theta[i]=rand()%1000/(double)10000;;      //隨機初始化theta
    }
}
int perceptron(double a)
{
    if(a>0)
        return 1;
    else if(a<0)
        return -1;
    else
    {
        cout<<"perceptron error";
        exit(0);
    }
}

void perceptron_rule()
{
    double loss=100;
    for(int i=0;i<iterator_n&&loss>=loss_theta;i++)
    {
        loss=0;//訓練誤差
        for(int j=0;j<DataRow;j++)
        {
            double error=0;
            for(int k=0;k<DataColumn;k++)
            {
                error+=DataTable[j][k]*Theta[k];
            }
            error=DataTable[j][DataColumn]-perceptron(error);    //計算t-o
            for(int k=0;k<DataColumn;k++)
            {
                Theta[k]+=learning_rate*error*DataTable[j][k];  //更新Theta
            }
            loss+=abs(error);
        }
    }
}
void printTheta()
{
    for(int i=0;i<DataColumn;i++)
        cout<<Theta[i]<<" ";
    cout<<endl;
}

int main()
{
    Init();
    perceptron_rule();
    printTheta();
    return 0;
}

結果

theta:

-0.1712 0.2836 0.2456

 

              

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM