機器學習-感知器學習算法


由於文章編輯問題,圖片和公式都無法展示,需要原文章和源代碼的,請下載
鏈接: http://pan.baidu.com/s/1dE8yK1N 密碼: 2c9n

機器學習-感知器學習算法

                                                                                                                        Python語言實現

                                                                                   初學者整理,參考資料均來源於網上

一、           概述

感知器是由美國計算機科學家羅森布拉特(Rosenblatt)於1957年提出的。感知器可謂是最早的人工神經網絡。

     感知器分單層感知器和多層感知器。

二、           單層感知器(Single Layer Perceptron

l 簡介

單層感知器是一個具有一層神經元、採用閾值激活函數的前向網絡。通過對網絡權值的訓練,可以使感知器對一組輸人矢量的響應達到元素爲01的目標輸出,從而實現對輸人矢量分類的目的。

單層感知器是一個簡單的線性二分類器,它保存着輸入權重,根據輸入和內置的函數計算輸出.人工神經網絡中的單個神經元。

單層感知器可以計算邏輯或,邏輯與,邏輯非等運算,但是不能計算異或。因爲異或不是平面線性可分的,在多層感知器中解決。

l 模型

單層感知器只有輸入和輸出,輸入和輸出直接相連,多個輸入一個輸出。

模型爲每個輸入定義爲X,輸入到輸出的權重定義爲w,所有的輸入和權重的乘積和爲輸出的值,對於這個乘積和做如下處理,如果乘機和大於臨界值(一般是0),輸入端就取1;如果小於臨界值,就取-1

l 工作原理

單層感知器可將外部輸入分爲兩類。當感知器的輸出爲+1時,輸入屬於L1,當感知器輸出-1時,輸入屬於L2類,從而實現兩類目標的識別。在二維空間,單層感知器進行模式識別的判決超平面由下式決定:

對於只有兩個輸入的判別邊界是直線(),選擇合適的學習算法可訓練出滿意的w1w2。將樣本中兩類數據用直線分開。如下圖:

b的值決定了直線的偏移量,w決定了直線的旋轉度數。

l 學習算法

第一步、設置變量和參數

f(x)爲激活函數,y(n)爲實際輸出,d(n)爲期望輸出,λ爲學習速率,n爲迭代次數,e爲實際輸出與期望輸出的誤差。

第二步、初始化

給權值向量w的各個分量賦一個較小的隨機非零值,置n=1

第三步、輸入一組樣本X(n)=[1,x1(n),x2(n),…,xm(n)],並給出它的期望輸出d(n)

第四步、計算出實際輸出:y(n)=f()

第五步、求出期望輸出和實際輸出的差 e=d(n)-y(n)。根據誤差判斷輸出是否滿足條件,一般爲對所有樣本誤差爲零或者均小於預設的值,則算法結束,否則將值增加1,並用下式調整權值:

w(n+1)=w(n)+λ[d(n)-y(n)]x(n)

權值調整公式屬於隨機梯度下降算法。(待確認)

然後轉到第三步,進行下一輪計算過程。

l 代碼(Python)

感知器源碼(Perceptron.py)

#單層感知器機器學習算法 20170621 gyk

 

#多層感知器使用梯度下降算法進行訓練

#單層感知器只能解決線性可分問題

 

#importlogging

#importos

 

#預測函數,訓練使用使用公式“輸入×權重的和”爲結果。使用階躍函數sigmoid把結果轉換爲10

#由於默認第一個輸入爲1,第一個權重爲b,所以在公式轉換爲:w1*x1+w2*x2+b=0

#結果等於0爲線性分割線的線上。小於0爲線下,大於0爲線上。用該線把結果分成兩種。

#由於在預測的時候,輸入參數比訓練參數少一個結果列,所以不能用輸入參數數組計算特徵列,而是用權重數組減去第一個bias偏移量列來計算特徵數。

defpredict(inputs, weights):

   activation = weights[0]

   for i in range(len(weights)-1):

       activation += weights[i+1]*inputs[i]

   return sigmoid(activation)

 

#階躍函數,把值轉換成1/0或者1/-1

defsigmoid(value):

   return 1.0 if value >= 0.0 else 0.0

 

#訓練

#初始化權重爲0.5,然後循環訓練集,讀取每一條訓練集數據,進行預測,然後把預測結果和實際結果進行對比,如果一樣就用下一條訓練。如果和實際結果不相等。就使用隨機梯度下降算法重新計算權重。

#然後重新訓練,直到所有訓練數據的預測結果和實際結果一直,則訓練結束。

#訓練的結果其實就是在平面上找一條直線,該直線能把兩類的數據點分割在直線的兩邊。如果有的點是錯誤的,就變化w1w2來旋轉直線,變化w0來移動直線找到最佳位置。訓練速率太大會找不到最佳位置,

#訓練速率太小,訓練速度就太慢。期望值和實際值的差值的正負決定了旋轉和移動的方向。

#l_rate爲學習速率,一般在00.1之間取值。

def train(dataSet,l_rate):

   count = 0

   weights = [0.5 for i inrange(len(dataSet[0]))]

   learningOK = False

   while not learningOK:

       if count >= 100:

           print('訓練已經超過100次,無法完成訓練')

           return None

       else:   

           count += 1

           

       learningOK = True

       for input_row in dataSet:

           prediction = predict(input_row,weights)

           error = input_row[-1] - prediction#期望輸出與實際輸出誤差

           if error != 0:

               weights = sgd(weights,input_row, l_rate, error)

               learningOK = False          

   return weights

 

#隨機梯度下降算法,計算新的權重,即改變分割線的位置

defsgd(weights, input_row, l_rate, error):

   for i in range(len(input_row)-1):

       weights[i+1] += l_rate * error*input_row[i]                       

   weights[0] += l_rate * error

   return weights

   

測試源碼(Perceptron_UnitTest.py)

#單層感知器單元測試 20170621 gyk

 

from impimport reload

fromPerceptron import *

 

#測試

deftest():

   l_rate = 0.2 #學習速率

   l_while = True

   dict_function ={'or':test_or,'and':test_and,'not':test_not,'xor':test_xor}

 

   while l_while:

       print('輸入q退出,輸入or實現或功能,輸入not實現非功能,輸入and實現與功能。輸入xor調用異或功能。或直接用命令行調用test_others函數可以實現其他預測')

       type_str = input()

       if type_str == 'q':

           l_while = False

       else:

           dict_function.get(type_str,'nothing')(l_rate)             

 

#計算或使用或數據集訓練完模型,實現或的效果。

deftest_or(l_rate):

       weights =train([[1,1,1],[1,0,1],[0,1,1],[0,0,0]] , l_rate)

       test_common([int(input('第一個參數')),int(input('第二個參數'))], weights)

 

#計算與使用或數據集訓練完模型,實現或的效果。

deftest_and(l_rate):

       weights =train([[1,1,1],[1,0,0],[0,1,0],[0,0,0]] , l_rate)

       test_common([int(input('第一個參數')),int(input('第二個參數'))],weights)

 

#計算異或使用異或數據集訓練完模型,實現異或的效果。單層感知器無法完成異或功能。

deftest_xor(l_rate):

       print('單層感知器無法完成異或功能')

       weights =train([[1,1,0],[1,0,1],[0,1,1],[0,0,0]] , l_rate)

       if weights != None:

           test_common([int(input('第一個參數')),int(input('第二個參數'))],weights)       

 

#計算非使用或數據集訓練完模型,實現或的效果。

deftest_not(l_rate):

       weights = train([[0,1],[1,0]] , l_rate)

       test_common([int(input('第一個參數'))], weights)

 

#計算通用

deftest_common(inputs, weights):

   if weights != None:

       print('結果爲:%d'%predict1(inputs, weights))

 

#訓練其他功能,只要輸入訓練集和待預測數據,就能預測出結果

deftest_others(trainDataSet, inputs):

       weights = train(trainDataSet , 0.2)

       test_common(inputs, weights)       

       

if__name__=='__main__':

   #logging.basicConfig(filename =os.path.join(os.getcwd(), 'log.txt'), level = logging.DEBUG)

   test() 

三、           多層感知器(Multi-Layer Perceptrons

l 簡介

多層感知機(Multi Layer Perceptron MLP)是由多個感知機層全連接組成的前饋神經網絡,這種模型在非線性問題中表現出色.

相對於單層感知器,輸出端從一個變到了多個;輸入端和輸出端之間也不光只有一層,增加了隱藏層。

所謂全連接是指層A上任一神經元與臨近層B上的任意神經元之間都存在連接.

反向傳播(Back PropagationBP)是誤差反向傳播的簡稱,這是一種用來訓練人工神經網絡的常見算法,通常與最優化方法(如梯度下降法)結合使用.多層感知器就是使用BP算法進行訓練的。

l 模型

l 工作原理

由前面介紹看到,單個感知器能夠完成線性可分數據的分類問題,是一種最簡單的可以學習的機器。但他無法解決非線性問題。比如下圖中的XOR問題:即(1,1)(-1,-1)屬於同一類,而(1,-1)(-1,1)屬於第二類的問題,不能由單個感知器正確分類。

https://img-blog.csdn.net/20130601150444390

單個感知器雖然無法解決異或問題,但卻可以通過將多個感知器組合,實現複雜空間的分割。如下圖:

https://img-blog.csdn.net/20130601150455577

將兩層感知器按照一定的結構和係數進行組合,第一層感知器實現兩個線性分類器,把特徵空間分割,而在這兩個感知器的輸出之上再加一層感知器,就可以實現異或運算。

也就是,由多個感知器組合:

https://img-blog.csdn.net/20130601151729163

來實現非線性分類面,其中θ(·)表示階躍函數或符號函數。

l 學習算法

經典的BP神經網絡通常由三層組成:輸入層,隱含層與輸出層.通常輸入層神經元的個數與特徵數相關,輸出層的個數與類別數相同,隱含層的層數與神經元數均可以自定義

http://images2015.cnblogs.com/blog/793413/201610/793413-20161010152810539-341221931.png

每個隱含層和輸出層神經元輸出與輸入的函數關係爲:

I j =∑ i W ij O i  Ij=∑iWijOi

O j =sigmod(I l )=11+e −I l    Oj=sigmod(Il)=11+e−Il

其中W ij  Wij表示神經元i與神經元j之間連接的權重,O j  Oj代表神經元j的輸出, sigmod是一個特殊的函數用於將任意實數映射到(01)區間.

上文中的sigmod函數稱爲神經元的激勵函數(activation function)除了sigmod函數11+e −I l    11+e−Il外,常用還有tanhReLU函數.

我們用一個完成訓練的神經網絡處理迴歸問題,每個樣本擁有n個輸入.相應地,神經網絡擁有n個輸入神經元和1個輸出神經元.

實際應用中我們通常在輸入層額外增加一個偏置神經元,提供一個可控的輸入修正;或者爲每個隱含層神經元設置一個偏置參數.

我們將n個特徵依次送入輸入神經元,隱含層神經元獲得輸入層的輸出並計算自己輸出值,輸出層的神經元根據隱含層輸出計算出迴歸值.

上述過程一般稱爲前饋(Feed-Forward)過程,該過程中神經網絡的輸入輸出與多維函數無異.

現在我們的問題是如何訓練這個神經網絡.

作爲監督學習算法,BP神經網絡的訓練過程即是根據前饋得到的預測值和參考值比較,根據誤差調整連接權重W ij  Wij的過程.

訓練過程稱爲反向傳播過程(BackPropagation)數據流正好與前饋過程相反.

首先我們隨機初始化連接權重W ij  Wij對某一訓練樣本進行一次前饋過程得到各神經元的輸出.

首先計算輸出層的誤差:

E j =sigmod ′ (O j )∗(T j −O j )=O j (1−O j )(T j −O j ) Ej=sigmod′(Oj)∗(Tj−Oj)=Oj(1−Oj)(Tj−Oj)

其中E j  Ej代表神經元j的誤差,O j  Oj表示神經元j的輸出,T j  Tj表示當前訓練樣本的參考輸出,sigmod ′ (x) sigmod′(x)是上文sigmod函數的一階導數.

計算隱含層誤差:

E j =sigmod ′ (O j )∗∑ k E k W jk =O j (1−O j )∑ k E k W jk  Ej=sigmod′(Oj)∗∑kEkWjk=Oj(1−Oj)∑kEkWjk

隱含層輸出不存在參考值,使用下一層誤差的加權和代替(T j −O j ) (Tj−Oj) .

計算完誤差後就可以更新W ij  Wijθ j  θj :

W ij =W ij +λE j O i  Wij=Wij+λEjOi

其中λ λ是一個稱爲學習率的參數,一般在(00.1)區間上取值.

實際上爲了加快學習的效率我們引入稱爲矯正矩陣的機制,矯正矩陣記錄上一次反向傳播過程中的E j O i  EjOi值,這樣W j  Wj更新公式變爲:

W ij =W ij +λE j O i +μC ij  Wij=Wij+λEjOi+μCij

μ μ是一個稱爲矯正率的參數.隨後更新矯正矩陣:

C ij =E j O i  Cij=EjOi

每一個訓練樣本都會更新一次整個網絡的參數.我們需要額外設置訓練終止的條件.

最簡單的訓練終止條件爲設置最大迭代次數,如將數據集迭代1000次後終止訓練.

單純的設置最大迭代次數不能保證訓練結果的精確度,更好的辦法是使用損失函數(loss function)作爲終止訓練的依據.

損失函數可以選用輸出層各節點的方差:

L=∑ j (T j −O j ) 2  L=∑j(Tj−Oj)2

爲了避免神經網絡進行無意義的迭代,我們通常在訓練數據集中抽出一部分用作校驗.當預測誤差高於閾值時提前終止訓練.

 

l 代碼

多層感知器源碼(MLP.py):

#多層感知器機器學習算法 20170621 gyk

 

#多層感知器使用BP算法進行訓練

 

#多層感知器,可以實現平面無法完成線性分割。把點看成是在立體空間中,用平面對他們進行分割

#隱藏節點數量的計算公式:s=[2*log2(m+n+1)]>=2 s爲隱藏節點數,m爲輸入特徵數,n爲識別出的類別數

 

import logging

import random

import math

 

class Mlp(object):

   

   #構造器 learn學習速率 limit最大循環次數 correct矯正率   

   def __init__(self, learn=0.05, limit=10000,correct=0.1):

       random.seed(0)

       self.__learn = learn

       self.__limit = limit

       self.__correct = correct

 

       self.__input_count = 0

       self.__hidden_count = 0

       self.__output_count = 0

 

       self.__inputs = []

       self.__hiddens = []

       self.__outputs = []

       self.__input_weights = []

       self.__output_weights = []

       self.__input_correction = []

       self.__output_correction = []     

       

   #初始化 input_count輸入節點數量 hidden_count隱藏節點數量 output_count輸出節點數量

   #輸入節點數量和特徵數量相關輸出節點數量和輸出類別數量相關

   def init(self, input_count, hidden_count,output_count):

       #初始化輸入,隱藏,輸出數量

       self.__input_count = input_count + 1

       self.__hidden_count = hidden_count

       self.__output_count = output_count

 

       #初始化輸入,隱藏,輸出節點的值

       self.__inputs = [1.0] *self.__input_count

       self.__hiddens = [1.0] *self.__hidden_count

       self.__outputs = [1.0] *self.__output_count

 

       #初始化存放一級,二級權重數組給權重賦值隨機數一級權重多加一列保存偏移量

       self.__input_weights =[[self.rand(-0.2, 0.2) for i in range(self.__hidden_count)]for j in range(self.__input_count)]

       self.__output_weights =[[self.rand(-2.0, 2.0) for i in range(self.__output_count)]for j inrange(self.__hidden_count)]

 

       #初始化糾正數組一級權重多加一列保存偏移量

       self.__input_correction =self.make_matrix(self.__input_count, self.__hidden_count)

       self.__output_correction =self.make_matrix(self.__hidden_count, self.__output_count)

       

   #構造二維數組   

   def make_matrix(self, row_count,column_count, value=0.0):

       mat = []

       for i in range(row_count):

           mat.append([value] * column_count)

       return mat

 

   #生成隨機數

   def rand(self, a, b):

       return (b - a) * random.random() + a

 

   #階躍函數

   def sigmoid(self, x):

       return 1.0 / (1.0 + math.exp(-x))

 

                   

   #階躍函數導數

   def sigmoid_derivate(self, x):

       return x * (1 - x)

 

   #訓練

   def train(self, trainDataSet, labels):

       leaningOK = False

       for i in range(self.__limit):

           if leaningOK: #無損失,退出,訓練完成

               return None

           

           error = 0.0

           for j in range(len(trainDataSet)):

               self.predict(trainDataSet[j])

               error +=self.back_propagate(labels[j])

           logging.debug("當前循環次數爲:%d當前損失函數錯誤值爲:%f"%(i,error))

           leaningOK = error <= 0.01

 

   #預測

   def predict(self, inputDataSet):

       #計算輸入層

       for i in range(self.__input_count-1):

           self.__inputs[i] = inputDataSet[i]

 

       #計算隱藏層

       for h in range(self.__hidden_count):

           total = 0.0        

           for i inrange(self.__input_count):               

               total += self.__inputs[i] *self.__input_weights[i][h]

           self.__hiddens[h] =self.sigmoid(total)

 

       #計算輸出層

       for o in range(self.__output_count):

           total = 0.0

           for h inrange(self.__hidden_count):

               total += self.__hiddens[h] *self.__output_weights[h][o]

           self.__outputs[o] =self.sigmoid(total)

 

       return self.__outputs[:]

 

   #反向傳播更新權值

   def back_propagate(self, labels):

       #計算輸出層誤差

       output_deltas = [0.0] *self.__output_count

       for o in range(self.__output_count):

           error = labels[o] -self.__outputs[o]

           output_deltas[o] =self.sigmoid_derivate(self.__outputs[o]) * error

 

       #計算隱藏層誤差

       hidden_deltas = [0.0] *self.__hidden_count

       for h in range(self.__hidden_count):

           error = 0.0

           for o inrange(self.__output_count):             

               error += output_deltas[o] *self.__output_weights[h][o]               

           hidden_deltas[h] =self.sigmoid_derivate(self.__hiddens[h]) * error

 

       #更新輸出對應的權值

       for h in range(self.__hidden_count):

           for o inrange(self.__output_count):

               change = output_deltas[o] *self.__hiddens[h]

               self.__output_weights[h][o] +=self.__learn * change + self.__correct * self.__output_correction[h][o]

               self.__output_correction[h][o]= change

 

       #更新輸入對應的權值

       for i inrange(self.__input_count):       

           for h inrange(self.__hidden_count):

               change = hidden_deltas[h] *self.__inputs[i]

               self.__input_weights[i][h] +=self.__learn * change + self.__correct * self.__input_correction[i][h]

               self.__input_correction[i][h] =change

 

       #返回錯誤總量

       error = 0.0

       for o in range(self.__output_count):

           error += 0.5 * (labels[o] -self.__outputs[o]) ** 2

       return error

      

測試源碼(MLP_UnitTest.py):

#多層感知器單元測試 20170621gyk

 

from imp import reload

from MLP import *

import logging

import os

 

#測試

def test():

   l_while = True

   dict_function = {'or':test_or,'xor':test_xor}

 

   whilel_while:

       print('輸入q退出,輸入or實現或功能,輸入xor調用異或功能。或直接用命令行調用test_others函數可以實現其他預測')

       type_str = input()

       if type_str == 'q':

           l_while = False

       else:

           dict_function.get(type_str,'nothing')()             

 

#計算或使用或數據集訓練完模型,實現或的效果。

def test_or():

       test_common([[1,1],[1,0],[0,1],[0,0]], [[1],[1],[1],[0]], [int(input('第一個參數')),int(input('第二個參數'))])

 

 

#計算異或使用異或數據集訓練完模型,實現異或的效果。

def test_xor():

       test_common([[1,1],[1,0],[0,1],[0,0]], [[0],[1],[1],[0]], [int(input('第一個參數')),int(input('第二個參數'))])       

 

#計算通用

def test_common(trainDataSet, labels,inputs):

   mlp = Mlp()

   mlp.init(2,5,1) #隱藏節點數量的計算公式:s=[2*log2(m+n+1)]>=2 s爲隱藏節點數,m爲輸入特徵數,n爲識別出的類別數

   mlp.train(trainDataSet, labels)

   print('結果爲:')

   for i in range(1):

       #print(mlp.predict(inputs)[i])

       print(1 if mlp.predict(inputs)[i]>=0.5 else 0)

 

#訓練其他功能,只要輸入訓練集和待預測數據,就能預測出結果

def test_others(trainDataSet, inputs):

       test_common(trainDataSet, inputs)       

       

if __name__=='__main__':

   #logging.basicConfig(filename = os.path.join(os.getcwd(), 'log.txt'),level = logging.DEBUG)

   test() 

 

 

 

 

 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章