神經網絡初試(二)

引入

今天我們就繼續(xù)上次的那篇文章,談一談神經網絡中比較基礎也比較核心的部分甜无。按順序分別是扛点,前向傳播陵究、反向傳播铜邮、權重更新以及代碼實現(xiàn)寨蹋。

在進入正題之前已旧,還是有必要介紹一些事情,方便對于下文的理解惊楼。

第一檀咙、本文,乃至本系列的文章璃诀,其主體都是全連接神經網絡攀芯。全連接神經網絡應該是神經網絡中最簡單的一種了。顧名思義文虏,全連接指的是對于任意一層的某個神經元來說,它與前后兩層的所有神經元之間都存在連接殖演。如圖所示:

這個神經網絡有三層氧秘,從左往右分別是之前提到過的輸入層、隱藏層趴久、輸出層丸相。每一層都有三個神經元,一般情況下彼棍,這代表著它的輸入應該是一個三維數據灭忠,也就是三維向量膳算,例如三維空間中任意一個坐標(x,y,z)。隱藏層的三個神經元弛作,把它當成處理數據的黑盒就好了涕蜂。輸出層的三個神經元,則代表神經網絡的輸出也是一個三維的數據映琳,例如它的輸出也是一個坐標(a,b,c)机隙。

之前我們也提到了偏置項,使用偏置項來表示輸入層之外的神經元的閾值葱跋,并且提到了,可以在每一層神經元(除了輸出層)上方添加一個偏置神經元,形式更加統(tǒng)一的表示神經網絡僵朗。上方的圖片是不包含偏置神經元的神經網絡結構圖,我們可以再看一個包含偏置神經元的神經網絡粪薛。


諾熟空,對于這個神經網絡而言掂咒,它的前兩層最上方的神經元都是偏置神經元,其輸入恒為1岁歉,它與下一層的神經元連接的權重,則表示著相應神經元的閾值。

當然啦努潘,倘若我們不把最上面兩個神經元看作偏置神經元,這就是一個非全連接的神經網絡压怠。這不是我們研究的對象~

第二、本文提到的神經網絡都是前饋的,也就是輸入層接受外界輸入, 隱含層與輸出層神經元對信號進行加工, 最終結果由輸出層神經元輸出栖忠。數據在其中是從輸入到輸出單向傳遞的,不會從輸出層又向輸入層傳回來。實際的應用中,自然也是存在這種可以反饋的冷溶,也就是信號回傳的神經網絡纯衍,但這也不是我們的研究對象。

所以歌亲,我們的研究對象就是全連接的,前饋的神經網絡悍缠,也就是最簡單的一種神經網絡。它是之后神經網絡各種變形的基礎趴拧,所以還是很有必要了解掌握的。

接下來進入正題。

前向傳播

前向傳播其實我們在上一篇文章中提過挂谍,所謂的前向傳播,其實就是神經網絡進行預測輸出時妄田,所進行的操作脚曾。我們以一個簡單的神經網絡為例再次說明這一過程,作為接下來的鋪墊拷沸。

上圖的神經網絡就是我們用于介紹前向傳播與反向傳播的主體啦勤庐。它的輸入層是一個二維向量(x_1,x_2),輸出也是一個二維向量(y_1,y_2)丈探,隱藏層有三個神經元(a_1,a_2,a_3)。一般來說,前向傳播過程中一個神經元對應一個一維數據爪幻。

對于權重挨稿,我們使用w_{ij}來表示,w_{ij}代表這一層第i個神經元與下一層第j個神經元之間的權重疲陕,是一個具體的數值。這里為了簡化邑蒋,我們暫時不把偏置項加進去钱慢,換句話說草描,我們將所有神經元的閾值都置為0。因此怀各,這是一個只有權重,閾值為0的全連接前饋神經網絡硕蛹。

除了權重和閾值鲤孵,激活函數也是神經網絡的核心之一琉兜。這里我們采用較為傳統(tǒng)的sigmoid函數作為激活函數,記為h(x)。因此幌氮,激活函數
h(x)=\frac{1}{1+e^{-x}}

接下來我們來敘述一下前向傳播的過程:

給定一個輸入向量(x_1,x_2)宇智,我們可以計算出隱藏層神經元的輸入向量,記為
X_{hidden}=[a_1;a_2;a_3]
X_{hidden}是一個列向量(本文以分號為分隔符的向量均為列向量),其中每一個分量的計算公式為
\begin{cases} a_1=w_{11}x_1+w_{21}x_2 \\ a_2=w_{12}x_1+w_{22}x_2 \\ a_3=w_{13}x_1+w_{23}x_2 \end{cases}
這個過程在上一篇文章中已經提到了,為了方便表示以及計算,我們可以使用矩陣來表示這一過程嬉挡。(沒學過線性代數的自己去學一下哦)

這個其實很簡單,我們將輸入向量(列向量)記為
I=[x_1;x_2]
這其實也是一個2×1的矩陣汇恤,兩行一列庞钢。類似的,隱藏層的輸入向量X_{hidden}是一個3×1的矩陣因谎。之后基括,我們記輸入層與隱藏層之間的矩陣為W_{input\_hidden}风皿,所以
W_{input\_hidden}=\begin{bmatrix} w_{11} & w_{21} \\ w_{12} & w_{22} \\ w_{13} & w_{23} \end{bmatrix}
這是一個3×2的矩陣侨艾。

所以
X_{hidden}=W_{input\_hidden} \cdot I
不過X_{hidden}僅僅是隱藏層的輸入磺芭,也就是隱藏層每一個神經元接收到的數據间螟。由于我們并沒有考慮閾值同波,所以這里直接對隱藏層的輸入數據進行激活悲雳,就得到了隱藏層每一個神經元的輸出税弃,即
O_{hidden}=h(X_{hidden})=sigmoid(X_{hidden})

這樣赋访,數據就由最原始的輸入I铣除,先轉化為隱藏層接收的輸入數據X_{hidden}盔腔,又轉化為了隱藏層的輸出O_{hidden}厉斟。

同理堂鲤,我們將隱藏層的輸出再傳遞到輸出層去译打。記隱藏層與輸出層之間的權重矩陣為
W_{hidden\_output}=\begin{bmatrix} w_{11}^{'} & w_{21}^{'} &w_{31}^{'} \\ w_{12}^{'} & w_{22}^{'} &w_{32}^{'}\\ \end{bmatrix}
其中w_{ij}^{'}表示隱藏層第i個神經元與輸出層第j個神經元之間的權重香到。

接著瑞妇,我們計算出輸出層接收到的二維向量為
X_{output}=W_{hidden\_output} \cdot O_{hidden}
最后聚谁,再經過激活函數,得到最終的輸出
O_{output}=sigmoid(X_{output})=[y_1;y_2]

就這樣,我們實現(xiàn)了上述神經網絡前向傳播的過程,及其矩陣表示的止,實現(xiàn)了從輸入到輸出的轉化檩坚。即
\begin{cases} X_{hidden}=W_{input\_hidden} \cdot I =W_{input\_hidden} \cdot [x_1;x_2]\\ O_{hidden}=h(X_{hidden})=sigmoid(X_{hidden}) \\ X_{output}=W_{hidden\_output} \cdot O_{hidden} \\ O_{output}=sigmoid(X_{output})=[y_1;y_2] \end{cases}

神經網絡的前向傳播,就是這么一個從x \longrightarrow y的過程诅福。當然匾委,這里的x,y都可以是任一維度的數據。

誤差反向傳播

前向傳播的過程氓润,是我們已知權重矩陣W赂乐,實現(xiàn)從輸入x到輸出y的過程。而誤差的反向傳播咖气,解決的則是已知輸入和輸出挨措,求解合適的權重矩陣W的過程。

之前提到過崩溪,機器學習三要素是模型浅役、策略和算法。放在當前語境下伶唯,模型就是上文中那個簡單的神經網絡觉既。策略,則是我們衡量誤差的手段乳幸,這里我們使用最經典的平方損失函數作為損失函數瞪讼,即我們的策略。記對于某個樣本(x,y)粹断,該神經網絡模型進行預測時的誤差為
E=\sum_{i=1}^n(y_i-o_i)^2
其中符欠,y_i表示樣本的實際輸出值y的第i個分量,o_i表示的是將樣本的實際輸入值x輸入神經網絡后瓶埋,輸出層第i個神經元的輸出值希柿,也就是y的第i個分量的預測值诊沪。對于上述的神經網絡,顯然曾撤,n=2娄徊。

問題來了,我們現(xiàn)在表示出了誤差盾戴,也知道了我們希望通過調整每一層的權重矩陣W來縮小這個誤差寄锐,那如何進行調整呢?

這里就不再引入了尖啡,直說吧橄仆,我們可以使用梯度下降法進行權重的調整。如果對于梯度下降法不太了解衅斩,可以看看我之前寫的那篇關于梯度下降的文章盆顾,或者自行搜索一下。

我們之前使用梯度下降法畏梆,是基于
f(x+\Delta x)=f(x)+\nabla f(x) \Delta x+o(\Delta x)
這里f(x)就是損失函數您宪,x就是參數,而\Delta x則是關于x進行的移動奠涌。對于一般的問題宪巨,比如一元線性回歸,可以直接構造出這樣一個等式溜畅,并且發(fā)現(xiàn)誤差下降的方向捏卓,就是負梯度-\nabla f(x)的方向。之后一步步進行迭代慈格,對于參數x進行調整怠晴。

但是對于神經網絡,問題就沒有這么簡單了浴捆。神經網絡是一層層疊加的蒜田,每兩層之間都有一個權重矩陣。但是我們可以觀察到的誤差选泻,僅僅是最后輸出的誤差冲粤,也就是預測值與實際值之間的誤差。雖然這一層的誤差是由之前的誤差層層傳遞而來滔金,但是我們很難直接通過最后一層的誤差色解,對最前幾層的權重矩陣進行調整茂嗓。假設一個神經網絡有10層餐茵,那么我們或許可以使用梯度下降,基于最后一層的誤差述吸,對最后一層的權重矩陣進行調整忿族,但是對于第一層和第二層之間的權重矩陣锣笨,又要怎么構建“誤差與權重”之間的關系呢?

當然道批,肯定是可以一步步構建的错英,但一定會很麻煩……因為中間涉及到的每一個權重矩陣,對于最后的誤差都有影響隆豹,所以如果想直接一步步推導回來椭岩,那么誤差E與第一個權重矩陣W_1的關系等式中,必然包含著W_2,W_3,...,W_9璃赡。感興趣的判哥,可以以一個三層的神經網絡,自己嘗試一下碉考。

因此塌计,我們的想法就是,將最終可以觀察到的誤差侯谁,一步步往回進行反向傳播锌仅,得到每一層的輸入值與輸出值之間的誤差,并基于此墙贱,使用梯度下降的方式對于相應的權重矩陣進行調整热芹。可以理解為惨撇,每兩層之間的權重矩陣都對應著一個誤差剿吻,這個誤差與相應的權重矩陣是直接相關的。這樣就不用一層層跋山涉水將距離很遠的誤差與權重直接構建關系啦串纺。

如上圖所示丽旅,e_4是我們可以直接觀察到的誤差(=實際值-預測值),它由e_{41},e_{42}這兩個分量組成纺棺,代表輸出層兩個神經元與實際值的誤差榄笙。e_4W_4有著直接的關系,因此我們可以通過e_4W_4進行修正祷蝌。同理茅撞,我們通過e_3,e_2,e_1分別對W_3,W_2,W_1進行調整。所以問題就轉化為巨朦,我們如何通過e_4米丘,求解得到e_3,e_2,e_1呢?

這就涉及到誤差的反向傳播啦糊啡,回到我們的那個模型上


對于這個三層的神經網絡拄查,我們可以觀察到的誤差是e_{21},e_{22},并且棚蓄,我們知道這兩層之間的權重堕扶,那如何求解第二層的誤差e_{11},e_{12},e_{13}呢碍脏?

簡單理解的話,我們可以把誤差的反向傳播過程稍算,看成誤差的分配過程典尾。對于誤差e_{21},與其直接有關的誤差分別是e_{11},e_{12},e_{13}糊探。在神經網絡前向傳播的過程中钾埂,e_{11},e_{12},e_{13}也通過權重w_{11},w_{21},w_{31}傳遞了過來,最終匯總形成了e_{21}科平。

我們用一個簡單的方式理解一下:不妨假設激活函數為h(x)=x勃教,則y_1=w_{11}a_1+w_{21}a_2+w_{31}a_3,那么由全微分公式
\Delta y_1=\frac{\partial y_1}{\partial a_1}\cdot \Delta a_1+\frac{\partial y_1}{\partial a_2}\cdot \Delta a_2+\frac{\partial y_1}{\partial a_3}\cdot \Delta a_3

\Delta y_1=w_{11}\cdot \Delta a_1+w_{21}\cdot \Delta a_2+w_{31}\cdot \Delta a_3
如果我們將\Delta看成相應的誤差匠抗,那么y_1的誤差其實就是a_1,a_2,a_3誤差的加權和故源。事實上誤差的傳播公式也是這么回事,感興趣的可以自行搜索一下。

因此,如果我們現(xiàn)在知道了\Delta y_1呆奕,想要求解\Delta a_1,\Delta a_2,\Delta a_3嘱朽,很自然的一個想法,就是根據權重再將\Delta y_1分解下去。即
\begin{cases} \Delta a_1=\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot \Delta y_1 \\ \Delta a_2=\frac{w_{21}}{w_{11}+w_{21}+w_{31}}\cdot \Delta y_1 \\ \Delta a_3=\frac{w_{31}}{w_{11}+w_{21}+w_{31}}\cdot \Delta y_1 \end{cases}
其實這樣的分解應該不是正確的,因為此時
\Delta y_1=w_{11}\cdot \Delta a_1+w_{21}\cdot \Delta a_2+w_{31}\cdot \Delta a_3
并不一定成立。但這是一個很合理的分配方式奶是,相信你也是這么認為的。

讓我們回到上述模型竣灌,則e_{11}e_{21}那里分到的誤差為
\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}
同理聂沙,它從e_{22}那里分到的誤差為
\frac{w_{12}}{w_{12}+w_{22}+w_{32}}\cdot e_{22}
由于e_{21}e_{22}本身并不存在直接關系,因此這兩者分給e_{11}的誤差直接相加初嘹,就可以得到
e_{11}=\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}+\frac{w_{12}}{w_{12}+w_{22}+w_{32}}\cdot e_{22}
以此類推
\begin{cases} e_{11}=\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}+\frac{w_{12}}{w_{12}+w_{22}+w_{32}}\cdot e_{22} \\ e_{12}=\frac{w_{21}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}+\frac{w_{22}}{w_{12}+w_{22}+w_{32}}\cdot e_{22} \\ e_{13}=\frac{w_{31}}{w_{11}+w_{21}+w_{31}}\cdot e_{21}+\frac{w_{32}}{w_{12}+w_{22}+w_{32}}\cdot e_{22} \end{cases}

舉個例子看一下


事實上及汉,我們更加關心的不是誤差本身,而是對于誤差的分配屯烦,即將后一層的誤差按照權重向前一層進行傳遞坷随。所以,為了進一步的簡化計算驻龟,我們直接將上式中的分子去掉温眉,即
\begin{cases} e_{11}=w_{11}\cdot e_{21}+w_{12}\cdot e_{22} \\ e_{12}=w_{21}\cdot e_{21}+w_{22}\cdot e_{22} \\ e_{13}=w_{31}\cdot e_{21}+w_{32}\cdot e_{22} \end{cases}
有些同學會對此感到迷惑,因為他們的分母并不完全一樣啊翁狐,怎么能說刪就刪呢类溢?可以這么想,e_{11}來自于兩個部分谴蔑,分別是e_{21}e_{22}豌骏,這兩者首先不是直接相關的,因此我們可以對于誤差直接相加隐锭,得到e_{11}窃躲。而其中來自于e_{21}的部分\frac{w_{11}}{w_{11}+w_{21}+w_{31}}\cdot e_{21},其分母是對于w_{11}的歸一化處理钦睡,我們可以將其轉化為w_{11}\cdot e_{21}蒂窒,這并不改變按權重分配的本質。同樣的荞怒,可以將\frac{w_{12}}{w_{12}+w_{22}+w_{32}}\cdot e_{22}轉化為w_{12}\cdot e_{22}洒琢。最后如之前一般再將二者直接相加,就得到了
e_{11}=w_{11}\cdot e_{21}+w_{12}\cdot e_{22}

明白了嗎褐桌?其實應該是似懂非懂衰抑,因為整個過程在數學上有些缺乏嚴謹性,更多的是從直觀理解的角度進行處理荧嵌,比如誤差直接按權分配呛踊,去除歸一化的因子等等。如果覺得不太能接受啦撮,那可以去看看更加數學化的誤差反向傳播方法的推導谭网。本文關于誤差反向傳播的說明,好像確實有種邪·教的感覺hhh赃春。但是本質上的思想應該是一樣的愉择。

最后,為了方便表示织中,我們記
\begin{alignat}{2} error_{output} &=[e_{21};e_{22}] \\ error_{hidden}&=[e_{11};e_{12};e_{13}] \end{alignat}
則由上文锥涕,我們知道
error_{hidden}=\begin{bmatrix} w_{11} & w_{12} \\ w_{21} & w_{22} \\ w_{31} &w_{32} \end{bmatrix} \cdot error_{output}

error_{hidden}=W_{hidden\_output}^{T} \cdot error_{output}

權重更新

以上我們(或許?)知道了誤差是如何進行反向傳播的狭吼,并進行了矩陣的表示站楚,接下來就可以根據誤差對權重進行調整啦。

首先搏嗡,對于神經網絡任意一層的誤差
E=\sum_{i=1}^n(y_i-o_i)^2
所以對于相應的權重矩陣中的任一個參數w_{j,k}
\frac{\partial E}{\partial w_{j,k}}=\frac{\partial \sum_{i=1}^n(y_i-o_i)^2}{\partial w_{j,k}}
再看一眼下圖窿春,我們會發(fā)現(xiàn),E中與w_{jk}有關的部分采盒,僅僅由后一層的第k個神經元有關旧乞,相應的誤差部分,也僅僅與第k個神經元的誤差有關磅氨。(例如下圖中w_{11}只與e_{21}有關)所以


\frac{\partial E}{\partial w_{j,k}}=\frac{\partial }{\partial w_{j,k}}(y_k-o_k)^2
根據鏈式法則(微積分中的求導法則都忘了尺栖?)
\begin{alignat}{2} \frac{\partial E}{\partial w_{j,k}} & =\frac{\partial E}{\partial o_k}\cdot \frac{\partial o_k}{\partial w_{j,k}} \\ & = -2(y_k-o_k)\cdot \frac{\partial o_k}{\partial w_{j,k}} \\ & =-2(y_k-o_k)\cdot \frac{\partial }{\partial w_{j,k}} sigmoid(\sum_{j}w_{jk}\cdot o_{j}) \end{alignat}
解釋一下o_{k}=sigmoid(\sum_{j}w_{jk}\cdot o_{j})這部分。首先烦租,o_k是后一層第k個輸出延赌,o_{j}則是前一層第j個節(jié)點的輸出除盏,w_{jk}k則是固定的。例如上圖挫以,對于w_{11}者蠕,它僅與e_{21}有關,而e_{21}相應神經元的輸出o_{1}掐松,則與前一層的三個神經元都有關系踱侣,就是前文提到的前向傳播啦。(說明:o_k=o_k^{l+1},o_j=o_j^l大磺,l表示神經網絡第l層)

對于sigmoid激活函數
\frac{\partial }{\partial x}sigmoid(x) =sigmoid(x)(1-sigmoid(x))
所以
\begin{alignat}{2} \frac{\partial E}{\partial w_{j,k}} & =-2(y_k-o_k)\cdot \frac{\partial }{\partial w_{j,k}} sigmoid(\sum_{j}w_{jk}\cdot o_{j}) \\ & = -2(y_k-o_k)\cdot \frac{\partial\ sigmoid(\sum_{j}w_{jk}\cdot o_{j})}{\sum_{j}w_{jk}\cdot o_{j}} \cdot \frac{\partial \sum_{j}w_{jk}\cdot o_{j}}{\partial w_{j,k}} \\ & = -2(y_k-o_k)\cdot sigmoid(\sum_{j}w_{jk}\cdot o_{j})\cdot (1-sigmoid(\sum_{j}w_{jk}\cdot o_{j}))\cdot o_j \end{alignat}
最后的o_j抡句,自然是上一層的第j個輸出啦,剛剛講過了杠愧。(說明:若o_k=o_k^{l+1},則o_j=o_j^l待榔,l表示神經網絡第l層)

考慮到在進行迭代時,導數前面的系數意義不大(還有學習率支撐嘛)流济,所以我們可以把式子中的2刪掉究抓,另外error=y_k-o_k,就得到了誤差關于權重的導數
\frac{\partial E}{\partial w_{j,k}}= -(error)\cdot sigmoid(\sum_{j}w_{jk}\cdot o_{j})\cdot (1-sigmoid(\sum_{j}w_{jk}\cdot o_{j}))\cdot o_j

相應的參數更新公式則為
new\ w_{j,k}=old\ w_{j,k} - \alpha \frac{\partial E}{\partial w_{j,k}}
其中\alpha為學習率袭灯。

但是W是一個權重矩陣刺下,所以我們有必要把上式也轉化成矩陣的形式,便于計算稽荧。以上文的那個三層神經網絡為例橘茉,第二層的權重更新公式為
W_{hidden\_output}=W_{hidden\_output}+\alpha*((error_{output}*O_{output}*(1-O_{output}))\cdot(O_{hidden}^{T}))
其中error_{output},O_{output},O_{hidden}均是上文提到過的前向傳播過程中的列向量。

相信你可以自己寫出W_{input\_hidden}的迭代公式了…

以上姨丈,參數的更新部分就全部結束了

MNIST數據集

接下來介紹一下MNIST手寫數字圖像集畅卓。MNIST是機器學習領域最有名的數據集之一,被應用于從簡單的實驗到發(fā)表的論文研究等各種場合蟋恬。MNIST數據集是由0到9的數字圖像構成的翁潘。訓練圖像有6萬張,測試圖像有1萬張歼争,主要分別用于訓練和評價拜马。

MNIST數據集的一般使用方法是,先用訓練圖像進行學習沐绒,再用學習到的模型度量能在多大程度上對測試圖像進行正確的分類俩莽。

MNIST的圖像數據是28像素 × 28像素的灰度圖像(1通道),各個像素的取值在0到255之間乔遮。每個圖像數據都相應地標有“2”扮超、“3”、“9”等標簽。因此出刷,這些圖像在進行分類時的輸入特征就是28*28=784個位置的像素灰度取值璧疗,輸出特征就是相應的最可能的數字標簽。

由于這類圖像是比較簡單的灰度圖馁龟,所以完全可以自己手寫一些數字崩侠,將其轉化成28×28的灰度圖像,進而進行驗證屁柏。更加直接地看到神經網絡的力量啦膜。

原始數據集網址:http://yann.lecun.com/exdb/mnist/

代碼實現(xiàn)

原始的數據集應該是無法直接使用的……這里提供一個網址:https://pjreddie.com/projects/mnist-in-csv/

該網站提供了將原始數據集轉化為csv格式的代碼有送,以及csv的文件淌喻。同時也是接下來的代碼使用到的數據集文件,如果需要的話自行下載吧雀摘。

代碼也放在下面了裸删,沒有依賴現(xiàn)成的第三方庫,完全是把上文的過程用代碼重述了一遍阵赠。如果你已經對于本文的內容很理解了涯塔,那么看這段代碼應該沒有任何問題。如果你看代碼還是有問題清蚀,不如再把文章讀一讀匕荸?

# -*- coding: utf-8 -*-
import numpy as np
import scipy.special
import matplotlib.pyplot as plt
class neuralNetwork:
    def __init__(self,inputnodes,hiddennodes,outputnodes,learningRate):
        #定義一個三層的神經網絡
        #初始化輸入層節(jié)點數量,隱藏層節(jié)點數量枷邪,輸出層節(jié)點數量
        self.inodes=inputnodes
        self.hnodes=hiddennodes
        self.onodes=outputnodes
        
        #初始化兩層之間的權重矩陣
        self.wih=(np.random.rand(self.hnodes,self.inodes)-0.5)
        self.who=(np.random.rand(self.onodes,self.hnodes)-0.5)
        
        #初始化激活函數
        self.activation_function=lambda x:scipy.special.expit(x)
        
        #初始化學習率
        self.lr=learningRate
        pass
    
    #訓練函數
    def train(self,inputs_list,targets_list):
        #將輸入的列表轉化為list便于計算
        inputs = np.array(inputs_list,ndmin=2).T
        targets=np.array(targets_list,ndmin=2).T
        
        #前向傳播一次榛搔,計算隱藏層的輸入和輸出
        hidden_inputs=np.dot(self.wih,inputs)
        hidden_outputs=self.activation_function(hidden_inputs)
        
        #再前向傳播一次,計算輸出層的輸入和輸出
        final_inputs=np.dot(self.who,hidden_outputs)
        final_outputs=self.activation_function(final_inputs)
        
        #確定輸出層的誤差與隱藏層的誤差
        output_errors=targets-final_outputs
        hidden_errors=np.dot(self.who.T,output_errors)
        
        #根據誤差东揣,使用梯度下降法調整權重
        self.who+=self.lr*np.dot((output_errors*final_outputs*(1.0-final_outputs)),np.transpose(hidden_outputs))
        self.wih+=self.lr*np.dot((hidden_errors*hidden_outputs*(1.0-hidden_outputs)),np.transpose(inputs))
        
        pass
    
    #查詢函數
    def query(self,inputs_list):
        #將列表轉化成矩陣
        inputs=np.array(inputs_list,ndmin=2).T
        
        #進行兩次前向傳播得到最終解
        hidden_inputs=np.dot(self.wih,inputs)
        hidden_outputs=self.activation_function(hidden_inputs)
        
        final_inputs=np.dot(self.who,hidden_outputs)
        final_outputs=self.activation_function(final_inputs)
        
        return final_outputs
    
input_nodes=784
hidden_nodes=100
output_nodes=10
lr=0.25
n=neuralNetwork(input_nodes,hidden_nodes,output_nodes,lr)
training_data_file=open("mnist_train.csv","r")
training_data_list=training_data_file.readlines()
training_data_file.close()
'''
for record in training_data_list:
    all_values=record.split(",")
    #ia=np.asfarray(all_values[1:]).reshape((28,28))
    #plt.imshow(ia,cmap="Greys",interpolation="None")  畫出圖像

    inputs=(np.asfarray(all_values[1:])/255.0*0.99)+0.01 #標準化處理
    targets=np.zeros(output_nodes)+0.01
    targets[int(all_values[0])]=0.99 #處理輸出向量
    n.train(inputs,targets)
    pass
'''   

epochs=4
for e in range(epochs):
    for record in training_data_list:
        all_values=record.split(",")
        #ia=np.asfarray(all_values[1:]).reshape((28,28))
        #plt.imshow(ia,cmap="Greys",interpolation="None")  畫出圖像
    
        inputs=(np.asfarray(all_values[1:])/255.0*0.99)+0.01 #標準化處理
        targets=np.zeros(output_nodes)+0.01
        targets[int(all_values[0])]=0.99 #處理輸出向量
        n.train(inputs,targets)
        pass


test_data_file=open("mnist_test.csv","r")
test_data_list=test_data_file.readlines()
test_data_file.close()

score=[]
for record in test_data_list:
    test_value=record.split(",")
    correct_label=int(test_value[0])
    #print(correct_label," correct_label")
    inputs=(np.asfarray(test_value[1:])/255.0*0.99)+0.01
    outputs=n.query(inputs)
    label=np.argmax(outputs)
    #print(label," network's answer")
    if(label==correct_label):
        score.append(1)
    else:
        score.append(0)
        pass
    pass
print("performance = ",sum(score)/len(score))

寫得累死我了……其實除了反向傳播那里有些邪·教外践惑,其他部分都還好,當然我的敘述可能是有一些啰嗦嘶卧。這篇文章比較適合完全不了解神經網絡的朋友了解一下神經網絡尔觉,其實并沒有想象的那么困難,幾十行代碼就可以實現(xiàn)一個簡單的神經網絡了芥吟。

如果深入學習的話侦铜,不妨去聽一聽吳恩達的深度學習,會更專業(yè)一些……不钟鸵,會專業(yè)很多泵额。畢竟這個神經網絡連偏置都沒有。

最后携添,不管怎樣嫁盲,感謝你看到這里,寫的不容易,看的就更不容易了……是不是應該把它拆成兩篇羞秤?

不管了缸托,就醬,bye~

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末瘾蛋,一起剝皮案震驚了整個濱河市俐镐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哺哼,老刑警劉巖佩抹,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異取董,居然都是意外死亡棍苹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門茵汰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枢里,“玉大人,你說我怎么就攤上這事蹂午±覆颍” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵豆胸,是天一觀的道長奥洼。 經常有香客問我,道長晚胡,這世上最難降的妖魔是什么灵奖? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮搬泥,結果婚禮上桑寨,老公的妹妹穿的比我還像新娘。我一直安慰自己忿檩,他們只是感情好尉尾,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著燥透,像睡著了一般沙咏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上班套,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天肢藐,我揣著相機與錄音,去河邊找鬼吱韭。 笑死吆豹,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播痘煤,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼凑阶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了衷快?” 一聲冷哼從身側響起宙橱,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蘸拔,沒想到半個月后师郑,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡调窍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年宝冕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陨晶。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡猬仁,死狀恐怖帝璧,靈堂內的尸體忽然破棺而出先誉,到底是詐尸還是另有隱情,我是刑警寧澤的烁,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布褐耳,位于F島的核電站,受9級特大地震影響渴庆,放射性物質發(fā)生泄漏铃芦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一襟雷、第九天 我趴在偏房一處隱蔽的房頂上張望刃滓。 院中可真熱鬧,春花似錦耸弄、人聲如沸咧虎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽砰诵。三九已至,卻和暖如春捌显,著一層夾襖步出監(jiān)牢的瞬間茁彭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工扶歪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留理肺,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像妹萨,于是被迫代替她去往敵國和親贪薪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容