Python手写神经网络实现3层感知机
一、BP神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)模型? ? ? ?
? ? ? ? BP算法的基本思想是,學(xué)習(xí)過程由信號(hào)的正向傳播和誤差的反向傳播倆個(gè)過程組成,輸入從輸入層輸入,經(jīng)隱層處理以后,傳向輸出層。如果輸出層的實(shí)際輸出和期望輸出不符合,就進(jìn)入誤差的反向傳播階段。誤差反向傳播是將輸出誤差以某種形式通過隱層向輸入層反向傳播,并將誤差分?jǐn)偨o各層的所有單元,從而獲得各層單元的誤差信號(hào),這個(gè)誤差信號(hào)就作為修正個(gè)單元權(quán)值的依據(jù)。知道輸出的誤差滿足一定條件或者迭代次數(shù)達(dá)到一定次數(shù)。
層與層之間為全連接,同一層之間沒有連接。結(jié)構(gòu)模型如下圖所示。
? ? ? ? 使用的傳遞函數(shù)sigmoid可微的特性使他可以使用梯度下降法。所以,在隱層函數(shù)中使用sigmoid函數(shù)作為傳遞函數(shù),在輸出層采用線性函數(shù)作為傳遞函數(shù)。
輸入向量、隱層輸出向量、最終輸出向量、期望輸出向量:
X=(x1,x2,x3……xn),其中圖中x0是為隱層神經(jīng)元引入閾值設(shè)置的;
Y=(y1,y2,y3……ym),其中圖中y0是為輸出神經(jīng)元引入閾值設(shè)置的;
O=(o1,o2,o3……ol)
D=(d1,d2,d3……dl)
輸出層的輸入是隱層的輸出,隱層的輸入是輸入層的輸出,計(jì)算方法和單層感知器的計(jì)算方法一樣。
單極性Sigmoid函數(shù):
雙極性sigmoid函數(shù):
二、BP神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)算法
? ? ? ? 標(biāo)準(zhǔn)BP神經(jīng)網(wǎng)絡(luò)沿著誤差性能函數(shù)梯度的反方向修改權(quán)值,原理與LMS算法比較類似,屬于最速下降法。此外還有以下改進(jìn)算法,如動(dòng)量最速下降法,擬牛頓法等。
??? 最速下降法又稱為梯度下降法。LMS算法就是最小均方誤差算法。LMS算法體現(xiàn)了糾錯(cuò)原則,與梯度下降法本質(zhì)上沒有區(qū)別,梯度下降法可以求目標(biāo)函數(shù)的極小值,如果將目標(biāo)函數(shù)取為均方誤差,就得到了LMS算法。
? ? ? ?梯度下降法原理:對(duì)于實(shí)值函數(shù)F(x),如果函數(shù)在某點(diǎn)x0處有定義且可微,則函數(shù)在該點(diǎn)處沿著梯度相反的方向下降最快,因此,使用梯度下降法時(shí),應(yīng)首先計(jì)算函數(shù)在某點(diǎn)處的梯度,再沿著梯度的反方向以一定的步長調(diào)整自變量的值。其中實(shí)值函數(shù)指的是傳遞函數(shù),自變量x指的是上一層權(quán)值和輸入值的點(diǎn)積作為的輸出值。
? ? ? ? 網(wǎng)絡(luò)誤差定義:
???
???
三層BP網(wǎng)絡(luò)算法推導(dǎo):
1、變量定義
網(wǎng)絡(luò)的實(shí)際輸出:
首先誤差反向傳播首先經(jīng)過輸出層,所以首先調(diào)整隱含層和輸出層之間的權(quán)值。
然后對(duì)輸入神經(jīng)元和隱層神經(jīng)元的誤差進(jìn)行調(diào)整。
權(quán)值矩陣的調(diào)整可以總結(jié)為:
??? 權(quán)值調(diào)整量det(w)=學(xué)習(xí)率*局部梯度*上一層輸出信號(hào)。
BP神經(jīng)網(wǎng)絡(luò)的復(fù)雜之處在于隱層輸入層、隱層和隱層之間的權(quán)值調(diào)整時(shí),局部梯度的計(jì)算需要用到上一步計(jì)算的結(jié)果,前一層的局部梯度是后一層局部梯度的加權(quán)和。
訓(xùn)練方式:
串行方式:網(wǎng)絡(luò)每獲得一個(gè)新樣本,就計(jì)算一次誤差并更新權(quán)值,直到樣本輸入完畢。
批量方式:網(wǎng)絡(luò)獲得所有的訓(xùn)練樣本,計(jì)算所有樣本均方誤差的和作為總誤差;
在串行運(yùn)行方式中,每個(gè)樣本依次輸入,需要的存儲(chǔ)空間更少,訓(xùn)練樣本的選擇是隨機(jī)的,可以降低網(wǎng)絡(luò)陷入局部最優(yōu)的可能性。
批量學(xué)習(xí)方式比串行方式更容易實(shí)現(xiàn)并行化。由于所有樣本同時(shí)參加運(yùn)算,因此批量方式的學(xué)習(xí)速度往往遠(yuǎn)優(yōu)于串行方式。
BP神經(jīng)網(wǎng)絡(luò)的優(yōu)點(diǎn):
BP神經(jīng)網(wǎng)絡(luò)的局限性:
??? 梯度下降法的缺陷:
BP神經(jīng)網(wǎng)絡(luò)的缺陷:
梯度下降法(最速下降法的改進(jìn)):
??? 針對(duì)算法的不足出現(xiàn)了幾種BP算法的改進(jìn)。
動(dòng)量法是在標(biāo)準(zhǔn)BP算法的權(quán)值更新階段引入動(dòng)量因子α(0<α<1),使權(quán)值修正具有一定慣性,可以看出,在原有的權(quán)值調(diào)整公式中,加入了動(dòng)量因子以及上一次的權(quán)值改變量。加入的動(dòng)量項(xiàng)表示本次權(quán)值的更新方向和幅度,不但與本次計(jì)算所得的梯度有關(guān),還與上一次更新的方向和幅度有關(guān)。動(dòng)量項(xiàng)反映了以前積累的調(diào)整經(jīng)驗(yàn),對(duì)于t時(shí)刻的調(diào)整起到了阻尼作用。當(dāng)誤差曲面出現(xiàn)驟然起伏時(shí),可減小震蕩趨勢(shì),提高訓(xùn)練速度。
在平緩區(qū)域希望學(xué)習(xí)率大一點(diǎn)減小迭代次數(shù),在坑凹處希望學(xué)習(xí)率小一點(diǎn),較小震蕩。所以,為了加速收斂過程,希望自適應(yīng)改變學(xué)習(xí)率,在該大的時(shí)候大,在該小的時(shí)候小。
學(xué)習(xí)率可變的BP算法是通過觀察誤差的增減來判斷的,當(dāng)誤差以減小的方式區(qū)域目標(biāo)時(shí),說明修正方向是正確的,可以增加學(xué)習(xí)率;當(dāng)誤差增加超過一定范圍時(shí),說明前一步修正進(jìn)行的不正確,應(yīng)減小步長,并撤銷前一步修正過程。學(xué)習(xí)率的增減通過乘以一個(gè)增量/減量因子實(shí)現(xiàn):
BP神經(jīng)網(wǎng)絡(luò)的設(shè)計(jì):
?? BP神經(jīng)網(wǎng)絡(luò)采用有監(jiān)督學(xué)習(xí)。解決具體問題時(shí),首先需要一個(gè)訓(xùn)練集。然后神經(jīng)網(wǎng)絡(luò)的設(shè)計(jì)主要包括網(wǎng)絡(luò)層數(shù)、輸入層節(jié)點(diǎn)數(shù)、隱層節(jié)點(diǎn)數(shù)、輸出層節(jié)點(diǎn)數(shù)、以及傳輸函數(shù)、訓(xùn)練方法、訓(xùn)練參數(shù)。
(一)輸入輸出數(shù)據(jù)的預(yù)處理:尺度變換。尺度變化也稱為歸一化或者標(biāo)準(zhǔn)化,是指變換處理將網(wǎng)絡(luò)的輸入、輸出數(shù)據(jù)限制在[0,1]或者[-1,1]區(qū)間內(nèi)。進(jìn)行變換的原因是,(1)網(wǎng)絡(luò)的各個(gè)輸入數(shù)據(jù)常常具有不同的物理意義和不同的量綱。尺度變換使所有分量都在一個(gè)區(qū)間內(nèi)變化,從而使網(wǎng)絡(luò)訓(xùn)練一開始就給各輸入分量以同等重要的地位;(2)BP神經(jīng)網(wǎng)絡(luò)神經(jīng)元均采用sigmoid函數(shù),變換后可防止因凈輸入的絕對(duì)值過大而使神經(jīng)元輸出飽和,繼而使權(quán)值調(diào)整進(jìn)入誤差曲面的平坦區(qū);(3)sigmoid函數(shù)輸出在區(qū)間[0,1]或者[-1,1]內(nèi),如果不對(duì)期望輸出數(shù)據(jù)進(jìn)行變換處理,勢(shì)必使數(shù)值大的分量絕對(duì)誤差大,數(shù)值小的分量絕對(duì)誤差小。
(二)神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)
1)網(wǎng)絡(luò)層數(shù) ?BP神經(jīng)網(wǎng)絡(luò)最多只需要倆個(gè)隱層,在設(shè)計(jì)的時(shí)候一般先只考慮設(shè)一個(gè)隱層,當(dāng)一個(gè)隱層的節(jié)點(diǎn)數(shù)很多但是依然不能改善網(wǎng)絡(luò)情況時(shí),才考慮增加一個(gè)隱層。經(jīng)驗(yàn)表明,如果在第一個(gè)隱層較多的節(jié)點(diǎn)數(shù),第二個(gè)隱層較少的節(jié)點(diǎn)數(shù),可以改善網(wǎng)絡(luò)性能。
2)輸入層節(jié)點(diǎn)數(shù) ?輸入層節(jié)點(diǎn)數(shù)取決于輸入向量的維數(shù)。應(yīng)用神經(jīng)網(wǎng)絡(luò)解決實(shí)際問題時(shí),首先應(yīng)從問題中提煉出一個(gè)抽象模型。如果輸入的是64*64的圖像,則輸入向量應(yīng)為圖像中左右的像素形成的4096維向量。如果待解決的問題是二院函數(shù)擬合,則輸入向量應(yīng)為二維向量。
3)隱層節(jié)點(diǎn)數(shù)設(shè)計(jì) ?隱含節(jié)點(diǎn)數(shù)對(duì)BP神經(jīng)網(wǎng)絡(luò)的性能有很大影響,一般較多的隱含層節(jié)點(diǎn)數(shù)可以帶來更好的性能,但是導(dǎo)致訓(xùn)練時(shí)間過長。通常是采用經(jīng)驗(yàn)公式給出估計(jì)值:
4)輸出層神經(jīng)元個(gè)數(shù) ?
5)傳遞函數(shù)的選擇 ?一般隱層選擇sigmoid函數(shù),輸出層選擇線性函數(shù)。如果也使用sigmoid函數(shù),則輸出值將會(huì)被限制在(0,1)或者(-1,1)之間。
6)訓(xùn)練方法的選擇 ?一般來說,對(duì)于包含數(shù)百個(gè)權(quán)值的函數(shù)逼近網(wǎng)絡(luò),使用LM算法收斂速度最快,均方誤差也小,但是LM算法對(duì)于模式識(shí)別相關(guān)問題的處理能力較弱,且需要較大的存儲(chǔ)空間。對(duì)于模式識(shí)別問題,使用RPROP算法能收到較好的效果。SCG算法對(duì)于模式識(shí)別和函數(shù)逼近都有較好的性能表現(xiàn)。串行方式需要更小的存儲(chǔ)空間,且輸入樣本具有一定隨機(jī)性,可以避免陷入局部最優(yōu)。批量方式的誤差收斂條件非常簡(jiǎn)單,訓(xùn)練速度快。
三、python實(shí)現(xiàn)加動(dòng)量法改進(jìn)
# -*- coding: utf-8 -*- """ Created on Mon Oct 1 22:15:54 2018@author: Heisenberg """ import numpy as np import math import random import string import matplotlib as mpl import matplotlib.pyplot as plt#random.seed(0) #當(dāng)我們?cè)O(shè)置相同的seed,每次生成的隨機(jī)數(shù)相同。如果不設(shè)置seed,則每次會(huì)生成不同的隨機(jī)數(shù)#參考https://blog.csdn.net/jiangjiang_jian/article/details/79031788#生成區(qū)間[a,b]內(nèi)的隨機(jī)數(shù) def random_number(a,b):return (b-a)*random.random()+a#生成一個(gè)矩陣,大小為m*n,并且設(shè)置默認(rèn)零矩陣 def makematrix(m, n, fill=0.0):a = []for i in range(m):a.append([fill]*n)return a#函數(shù)sigmoid(),這里采用tanh,因?yàn)榭雌饋硪葮?biāo)準(zhǔn)的sigmoid函數(shù)好看 def sigmoid(x):return math.tanh(x)#函數(shù)sigmoid的派生函數(shù) def derived_sigmoid(x):return 1.0 - x**2#構(gòu)造三層BP網(wǎng)絡(luò)架構(gòu) class BPNN:def __init__(self, num_in, num_hidden, num_out):#輸入層,隱藏層,輸出層的節(jié)點(diǎn)數(shù)self.num_in = num_in + 1 #增加一個(gè)偏置結(jié)點(diǎn)self.num_hidden = num_hidden + 1 #增加一個(gè)偏置結(jié)點(diǎn)self.num_out = num_out#激活神經(jīng)網(wǎng)絡(luò)的所有節(jié)點(diǎn)(向量)self.active_in = [1.0]*self.num_inself.active_hidden = [1.0]*self.num_hiddenself.active_out = [1.0]*self.num_out#創(chuàng)建權(quán)重矩陣self.wight_in = makematrix(self.num_in, self.num_hidden)self.wight_out = makematrix(self.num_hidden, self.num_out)#對(duì)權(quán)值矩陣賦初值for i in range(self.num_in):for j in range(self.num_hidden):self.wight_in[i][j] = random_number(-0.2, 0.2)for i in range(self.num_hidden):for j in range(self.num_out):self.wight_out[i][j] = random_number(-0.2, 0.2)#最后建立動(dòng)量因子(矩陣)self.ci = makematrix(self.num_in, self.num_hidden)self.co = makematrix(self.num_hidden, self.num_out) #信號(hào)正向傳播def update(self, inputs):if len(inputs) != self.num_in-1:raise ValueError('與輸入層節(jié)點(diǎn)數(shù)不符')#數(shù)據(jù)輸入輸入層for i in range(self.num_in - 1):#self.active_in[i] = sigmoid(inputs[i]) #或者先在輸入層進(jìn)行數(shù)據(jù)處理self.active_in[i] = inputs[i] #active_in[]是輸入數(shù)據(jù)的矩陣#數(shù)據(jù)在隱藏層的處理for i in range(self.num_hidden - 1):sum = 0.0for j in range(self.num_in):sum = sum + self.active_in[i] * self.wight_in[j][i]self.active_hidden[i] = sigmoid(sum) #active_hidden[]是處理完輸入數(shù)據(jù)之后存儲(chǔ),作為輸出層的輸入數(shù)據(jù)#數(shù)據(jù)在輸出層的處理for i in range(self.num_out):sum = 0.0for j in range(self.num_hidden):sum = sum + self.active_hidden[j]*self.wight_out[j][i]self.active_out[i] = sigmoid(sum) #與上同理return self.active_out[:]#誤差反向傳播def errorbackpropagate(self, targets, lr, m): #lr是學(xué)習(xí)率, m是動(dòng)量因子if len(targets) != self.num_out:raise ValueError('與輸出層節(jié)點(diǎn)數(shù)不符!')#首先計(jì)算輸出層的誤差out_deltas = [0.0]*self.num_outfor i in range(self.num_out):error = targets[i] - self.active_out[i]out_deltas[i] = derived_sigmoid(self.active_out[i])*error#然后計(jì)算隱藏層誤差hidden_deltas = [0.0]*self.num_hiddenfor i in range(self.num_hidden):error = 0.0for j in range(self.num_out):error = error + out_deltas[j]* self.wight_out[i][j]hidden_deltas[i] = derived_sigmoid(self.active_hidden[i])*error#首先更新輸出層權(quán)值for i in range(self.num_hidden):for j in range(self.num_out):change = out_deltas[j]*self.active_hidden[i]self.wight_out[i][j] = self.wight_out[i][j] + lr*change + m*self.co[i][j]self.co[i][j] = change#然后更新輸入層權(quán)值for i in range(self.num_in):for i in range(self.num_hidden):change = hidden_deltas[j]*self.active_in[i]self.wight_in[i][j] = self.wight_in[i][j] + lr*change + m* self.ci[i][j]self.ci[i][j] = change#計(jì)算總誤差error = 0.0for i in range(len(targets)):error = error + 0.5*(targets[i] - self.active_out[i])**2return error#測(cè)試def test(self, patterns):for i in patterns:print(i[0], '->', self.update(i[0]))#權(quán)重def weights(self):print("輸入層權(quán)重")for i in range(self.num_in):print(self.wight_in[i])print("輸出層權(quán)重")for i in range(self.num_hidden):print(self.wight_out[i])def train(self, pattern, itera=100000, lr = 0.1, m=0.1):for i in range(itera):error = 0.0for j in pattern:inputs = j[0]targets = j[1]self.update(inputs)error = error + self.errorbackpropagate(targets, lr, m)if i % 100 == 0:print('誤差 %-.5f' % error)#實(shí)例 def demo():patt = [[[1,2,5],[0]],[[1,3,4],[1]],[[1,6,2],[1]],[[1,5,1],[0]],[[1,8,4],[1]]]#創(chuàng)建神經(jīng)網(wǎng)絡(luò),3個(gè)輸入節(jié)點(diǎn),3個(gè)隱藏層節(jié)點(diǎn),1個(gè)輸出層節(jié)點(diǎn)n = BPNN(3, 3, 1)#訓(xùn)練神經(jīng)網(wǎng)絡(luò)n.train(patt)#測(cè)試神經(jīng)網(wǎng)絡(luò)n.test(patt)#查閱權(quán)重值n.weights()if __name__ == '__main__':demo()計(jì)算結(jié)果:
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的Python手写神经网络实现3层感知机的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python:通过SNMP协议获取H3C
- 下一篇: python自己做个定时器_python