[轉] 一文弄懂神經網絡中的反向傳播法——BackPropagation

在看CNN和RNN的相關算法TF實現,總感覺有些細枝末節理解不到位,浮在表面。那么就一點點扣細節吧。

這個作者講方向傳播也是沒誰了,666~

原文地址:https://www.cnblogs.com/charlotte77/p/5629865.html


  最近在看深度學習的東西,一開始看的吳恩達的UFLDL教程,有中文版就直接看了,后來發現有些地方總是不是很明確,又去看英文版,然后又找了些資料看,才發現,中文版的譯者在翻譯的時候會對省略的公式推導過程進行補充,但是補充的又是錯的,難怪覺得有問題。反向傳播法其實是神經網絡的基礎了,但是很多人在學的時候總是會遇到一些問題,或者看到大篇的公式覺得好像很難就退縮了,其實不難,就是一個鏈式求導法則反復用。如果不想看公式,可以直接把數值帶進去,實際的計算一下,體會一下這個過程之后再來推導公式,這樣就會覺得很容易了。

  說到神經網絡,大家看到這個圖應該不陌生:

?

  這是典型的三層神經網絡的基本構成,Layer L1是輸入層,Layer L2是隱含層,Layer L3是隱含層,我們現在手里有一堆數據{x1,x2,x3,...,xn},輸出也是一堆數據{y1,y2,y3,...,yn},現在要他們在隱含層做某種變換,讓你把數據灌進去后得到你期望的輸出。如果你希望你的輸出和原始輸入一樣,那么就是最常見的自編碼模型(Auto-Encoder)。可能有人會問,為什么要輸入輸出都一樣呢?有什么用啊?其實應用挺廣的,在圖像識別,文本分類等等都會用到,我會專門再寫一篇Auto-Encoder的文章來說明,包括一些變種之類的。如果你的輸出和原始輸入不一樣,那么就是很常見的人工神經網絡了,相當于讓原始數據通過一個映射來得到我們想要的輸出數據,也就是我們今天要講的話題。

  本文直接舉一個例子,帶入數值演示反向傳播法的過程,公式的推導等到下次寫Auto-Encoder的時候再寫,其實也很簡單,感興趣的同學可以自己推導下試試:)(注:本文假設你已經懂得基本的神經網絡構成,如果完全不懂,可以參考Poll寫的筆記:[Mechine Learning & Algorithm] 神經網絡基礎)

  假設,你有這樣一個網絡層:

  第一層是輸入層,包含兩個神經元i1,i2,和截距項b1;第二層是隱含層,包含兩個神經元h1,h2和截距項b2,第三層是輸出o1,o2,每條線上標的wi是層與層之間連接的權重,激活函數我們默認為sigmoid函數。

  現在對他們賦上初值,如下圖:

  其中,輸入數據 ?i1=0.05,i2=0.10;

     輸出數據 o1=0.01,o2=0.99;

     初始權重 ?w1=0.15,w2=0.20,w3=0.25,w4=0.30;

          ?w5=0.40,w6=0.45,w7=0.50,w8=0.55

?

  目標:給出輸入數據i1,i2(0.05和0.10),使輸出盡可能與原始輸出o1,o2(0.01和0.99)接近。

?

  Step 1 前向傳播

  1.輸入層---->隱含層:

  計算神經元h1的輸入加權和:

神經元h1的輸出o1:(此處用到激活函數為sigmoid函數):

?

?

  同理,可計算出神經元h2的輸出o2:

  

?

  2.隱含層---->輸出層:

  計算輸出層神經元o1和o2的值:

  

?

這樣前向傳播的過程就結束了,我們得到輸出值為[0.75136079 , 0.772928465],與實際值[0.01 , 0.99]相差還很遠,現在我們對誤差進行反向傳播,更新權值,重新計算輸出。

?

Step 2 反向傳播

1.計算總誤差

總誤差:(square error)

但是有兩個輸出,所以分別計算o1和o2的誤差,總誤差為兩者之和:

?

2.隱含層---->輸出層的權值更新:

以權重參數w5為例,如果我們想知道w5對整體誤差產生了多少影響,可以用整體誤差對w5求偏導求出:(鏈式法則)

下面的圖可以更直觀的看清楚誤差是怎樣反向傳播的:

現在我們來分別計算每個式子的值:

計算

計算

(這一步實際上就是對sigmoid函數求導,比較簡單,可以自己推導一下)

?

計算

最后三者相乘:

這樣我們就計算出整體誤差E(total)對w5的偏導值。

回過頭來再看看上面的公式,我們發現:

為了表達方便,用來表示輸出層的誤差:

因此,整體誤差E(total)對w5的偏導公式可以寫成:

如果輸出層誤差計為負的話,也可以寫成:

最后我們來更新w5的值:

(其中,是學習速率,這里我們取0.5)

同理,可更新w6,w7,w8:

?

3.隱含層---->隱含層的權值更新:

 方法其實與上面說的差不多,但是有個地方需要變一下,在上文計算總誤差對w5的偏導時,是從out(o1)---->net(o1)---->w5,但是在隱含層之間的權值更新時,是out(h1)---->net(h1)---->w1,而out(h1)會接受E(o1)和E(o2)兩個地方傳來的誤差,所以這個地方兩個都要計算。

?

?

計算

先計算

同理,計算出:

          

兩者相加得到總值:

再計算

再計算

最后,三者相乘:

?為了簡化公式,用sigma(h1)表示隱含層單元h1的誤差:

最后,更新w1的權值:

同理,額可更新w2,w3,w4的權值:

?

  這樣誤差反向傳播法就完成了,最后我們再把更新的權值重新計算,不停地迭代,在這個例子中第一次迭代之后,總誤差E(total)由0.298371109下降至0.291027924。迭代10000次后,總誤差為0.000035085,輸出為[0.015912196,0.984065734](原輸入為[0.01,0.99]),證明效果還是不錯的。

?

代碼(Python):

復制代碼
  1 #coding:utf-82 import random3 import math4 5 #6 #   參數解釋:7 #   "pd_" :偏導的前綴8 #   "d_" :導數的前綴9 #   "w_ho" :隱含層到輸出層的權重系數索引10 #   "w_ih" :輸入層到隱含層的權重系數的索引11 12 class NeuralNetwork:13     LEARNING_RATE = 0.514 15     def __init__(self, num_inputs, num_hidden, num_outputs, hidden_layer_weights = None, hidden_layer_bias = None, output_layer_weights = None, output_layer_bias = None):16         self.num_inputs = num_inputs17 18         self.hidden_layer = NeuronLayer(num_hidden, hidden_layer_bias)19         self.output_layer = NeuronLayer(num_outputs, output_layer_bias)20 21         self.init_weights_from_inputs_to_hidden_layer_neurons(hidden_layer_weights)22         self.init_weights_from_hidden_layer_neurons_to_output_layer_neurons(output_layer_weights)23 24     def init_weights_from_inputs_to_hidden_layer_neurons(self, hidden_layer_weights):25         weight_num = 026         for h in range(len(self.hidden_layer.neurons)):27             for i in range(self.num_inputs):28                 if not hidden_layer_weights:29                     self.hidden_layer.neurons[h].weights.append(random.random())30                 else:31                     self.hidden_layer.neurons[h].weights.append(hidden_layer_weights[weight_num])32                 weight_num += 133 34     def init_weights_from_hidden_layer_neurons_to_output_layer_neurons(self, output_layer_weights):35         weight_num = 036         for o in range(len(self.output_layer.neurons)):37             for h in range(len(self.hidden_layer.neurons)):38                 if not output_layer_weights:39                     self.output_layer.neurons[o].weights.append(random.random())40                 else:41                     self.output_layer.neurons[o].weights.append(output_layer_weights[weight_num])42                 weight_num += 143 44     def inspect(self):45         print('------')46         print('* Inputs: {}'.format(self.num_inputs))47         print('------')48         print('Hidden Layer')49         self.hidden_layer.inspect()50         print('------')51         print('* Output Layer')52         self.output_layer.inspect()53         print('------')54 55     def feed_forward(self, inputs):56         hidden_layer_outputs = self.hidden_layer.feed_forward(inputs)57         return self.output_layer.feed_forward(hidden_layer_outputs)58 59     def train(self, training_inputs, training_outputs):60         self.feed_forward(training_inputs)61 62         # 1. 輸出神經元的值63         pd_errors_wrt_output_neuron_total_net_input = [0] * len(self.output_layer.neurons)64         for o in range(len(self.output_layer.neurons)):65 66             # ?E/?z?67             pd_errors_wrt_output_neuron_total_net_input[o] = self.output_layer.neurons[o].calculate_pd_error_wrt_total_net_input(training_outputs[o])68 69         # 2. 隱含層神經元的值70         pd_errors_wrt_hidden_neuron_total_net_input = [0] * len(self.hidden_layer.neurons)71         for h in range(len(self.hidden_layer.neurons)):72 73             # dE/dy? = Σ ?E/?z? * ?z/?y? = Σ ?E/?z? * w??74             d_error_wrt_hidden_neuron_output = 075             for o in range(len(self.output_layer.neurons)):76                 d_error_wrt_hidden_neuron_output += pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].weights[h]77 78             # ?E/?z? = dE/dy? * ?z?/?79             pd_errors_wrt_hidden_neuron_total_net_input[h] = d_error_wrt_hidden_neuron_output * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_input()80 81         # 3. 更新輸出層權重系數82         for o in range(len(self.output_layer.neurons)):83             for w_ho in range(len(self.output_layer.neurons[o].weights)):84 85                 # ?E?/?w?? = ?E/?z? * ?z?/?w??86                 pd_error_wrt_weight = pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].calculate_pd_total_net_input_wrt_weight(w_ho)87 88                 # Δw = α * ?E?/?w?89                 self.output_layer.neurons[o].weights[w_ho] -= self.LEARNING_RATE * pd_error_wrt_weight90 91         # 4. 更新隱含層的權重系數92         for h in range(len(self.hidden_layer.neurons)):93             for w_ih in range(len(self.hidden_layer.neurons[h].weights)):94 95                 # ?E?/?w? = ?E/?z? * ?z?/?w?96                 pd_error_wrt_weight = pd_errors_wrt_hidden_neuron_total_net_input[h] * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_weight(w_ih)97 98                 # Δw = α * ?E?/?w?99                 self.hidden_layer.neurons[h].weights[w_ih] -= self.LEARNING_RATE * pd_error_wrt_weight
100 
101     def calculate_total_error(self, training_sets):
102         total_error = 0
103         for t in range(len(training_sets)):
104             training_inputs, training_outputs = training_sets[t]
105             self.feed_forward(training_inputs)
106             for o in range(len(training_outputs)):
107                 total_error += self.output_layer.neurons[o].calculate_error(training_outputs[o])
108         return total_error
109 
110 class NeuronLayer:
111     def __init__(self, num_neurons, bias):
112 
113         # 同一層的神經元共享一個截距項b
114         self.bias = bias if bias else random.random()
115 
116         self.neurons = []
117         for i in range(num_neurons):
118             self.neurons.append(Neuron(self.bias))
119 
120     def inspect(self):
121         print('Neurons:', len(self.neurons))
122         for n in range(len(self.neurons)):
123             print(' Neuron', n)
124             for w in range(len(self.neurons[n].weights)):
125                 print('  Weight:', self.neurons[n].weights[w])
126             print('  Bias:', self.bias)
127 
128     def feed_forward(self, inputs):
129         outputs = []
130         for neuron in self.neurons:
131             outputs.append(neuron.calculate_output(inputs))
132         return outputs
133 
134     def get_outputs(self):
135         outputs = []
136         for neuron in self.neurons:
137             outputs.append(neuron.output)
138         return outputs
139 
140 class Neuron:
141     def __init__(self, bias):
142         self.bias = bias
143         self.weights = []
144 
145     def calculate_output(self, inputs):
146         self.inputs = inputs
147         self.output = self.squash(self.calculate_total_net_input())
148         return self.output
149 
150     def calculate_total_net_input(self):
151         total = 0
152         for i in range(len(self.inputs)):
153             total += self.inputs[i] * self.weights[i]
154         return total + self.bias
155 
156     # 激活函數sigmoid
157     def squash(self, total_net_input):
158         return 1 / (1 + math.exp(-total_net_input))
159 
160 
161     def calculate_pd_error_wrt_total_net_input(self, target_output):
162         return self.calculate_pd_error_wrt_output(target_output) * self.calculate_pd_total_net_input_wrt_input();
163 
164     # 每一個神經元的誤差是由平方差公式計算的
165     def calculate_error(self, target_output):
166         return 0.5 * (target_output - self.output) ** 2
167 
168     
169     def calculate_pd_error_wrt_output(self, target_output):
170         return -(target_output - self.output)
171 
172     
173     def calculate_pd_total_net_input_wrt_input(self):
174         return self.output * (1 - self.output)
175 
176 
177     def calculate_pd_total_net_input_wrt_weight(self, index):
178         return self.inputs[index]
179 
180 
181 # 文中的例子:
182 
183 nn = NeuralNetwork(2, 2, 2, hidden_layer_weights=[0.15, 0.2, 0.25, 0.3], hidden_layer_bias=0.35, output_layer_weights=[0.4, 0.45, 0.5, 0.55], output_layer_bias=0.6)
184 for i in range(10000):
185     nn.train([0.05, 0.1], [0.01, 0.09])
186     print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.09]]]), 9))
187 
188 
189 #另外一個例子,可以把上面的例子注釋掉再運行一下:
190 
191 # training_sets = [
192 #     [[0, 0], [0]],
193 #     [[0, 1], [1]],
194 #     [[1, 0], [1]],
195 #     [[1, 1], [0]]
196 # ]
197 
198 # nn = NeuralNetwork(len(training_sets[0][0]), 5, len(training_sets[0][1]))
199 # for i in range(10000):
200 #     training_inputs, training_outputs = random.choice(training_sets)
201 #     nn.train(training_inputs, training_outputs)
202 #     print(i, nn.calculate_total_error(training_sets))
復制代碼

?

  

  最后寫到這里就結束了,現在還不會用latex編輯數學公式,本來都直接想寫在草稿紙上然后掃描了傳上來,但是覺得太影響閱讀體驗了。以后會用公式編輯器后再重把公式重新編輯一遍。穩重使用的是sigmoid激活函數,實際還有幾種不同的激活函數可以選擇,具體的可以參考文獻[3],最后推薦一個在線演示神經網絡變化的網址:http://www.emergentmind.com/neural-network,可以自己填輸入輸出,然后觀看每一次迭代權值的變化,很好玩~如果有錯誤的或者不懂的歡迎留言:)

?

參考文獻:

1.Poll的筆記:[Mechine Learning & Algorithm] 神經網絡基礎(http://www.cnblogs.com/maybe2030/p/5597716.html#3457159 )

2.Rachel_Zhang:http://blog.csdn.net/abcjennifer/article/details/7758797

3.http://www.cedar.buffalo.edu/%7Esrihari/CSE574/Chap5/Chap5.3-BackProp.pdf

4.https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/

?

------------------------------------本博客所有內容以學習、研究和分享為主,如需轉載,請聯系本人,標明作者和出處,并且是非商業用途,謝謝!--------------------------------

?

?

作者:Charlotte77?

出處:http://www.cnblogs.com/charlotte77/?

轉載于:https://www.cnblogs.com/Arborday/p/9039060.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/451759.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/451759.shtml
英文地址,請注明出處:http://en.pswp.cn/news/451759.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java線程組

線程組 線程組是Java線程編程所持有的概念。在Java中,線程組是指java.lang.ThreadGroup類的對象,每個線程都隸屬于唯一的一個線程組,這個線程組在線程創建時指定并在線程的整個生命周期內都不能更改。可以通過調用包含ThreadGroup類型參數的T…

FreeBSD 8.3 發布

近日,FreeBSD開發團隊放出了8.x穩定分支的8.3版本。此次發行的版本將支持amd64、i386、pc98和 sparc64等處理器類型。FreeBSD是一種類UNIX操作系統,但不是真正意義上的 UNIX 操作系統,它是由經過 BSD、386BSD 和 4.4BSD 發展而來的 Unix 的一…

Java中四種訪問權限總結

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 一、Java中有四種訪問權限, 其中三種有訪問權限修飾符,分別為private、public、protected,還有一種不…

28--僅僅反轉字母

文章目錄1.問題描述2.代碼詳情1.問題描述 給定一個字符串 S,返回 “反轉后的” 字符串,其中不是字母的字符都保留在原地,而所有字母的位置發生反轉。 示例 1: 輸入:“ab-cd” 輸出:“dc-ba” 示例 2&…

Moving Average

移動平均算法Demo #!/usr/bin/python2.7 # Fetch data from BD and analyse.import json import urllib import traceback import numpy as np # import pandas as pd import matplotlib.pyplot as plt #from scipy import statsdef fetch_raw_data(url):try:response urllib.…

【前端工程師手冊】JavaScript作用域拾遺

【前端工程師手冊】JavaScript作用域拾遺 昨天總結了一些作用域的知識【前端工程師手冊】JavaScript之作用域,但是發表完發現忘記了一些東西,今天拾個遺。 昨天說到了JavaScript中沒有塊級作用域,其實在es6中是有的。 es6中的塊級作用域 先舉…

游戲開發中的數據表示

聲明:本文內容源自騰訊游戲學院程序公開課_服務端 一、數據表示的基礎 什么是數據表示? 數據是信息的載體。 數據表示是一組操作,可以描述、顯示、操作信息。 數據表示的要素 IDL - 接口描述語言 IDL是用來描述軟件組件接口的一種計算機語言。…

29--反轉字符串

文章目錄1.問題描述2.代碼詳情1.問題描述 編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 char[] 的形式給出。 不要給另外的數組分配額外的空間,你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。 你可以假設數組中…

什么是臨界區

臨界區[1] 指的是一個訪問共用資源(例如:共用設備或是共用存儲器)的程序片段,而這些共用資源又無法同時被多個 線程 訪問的特性。當有線程進入臨界區段時,其他線程或是 進程 必須等待(例如:bo…

BZOJ 2957 樓房重建 (分塊)

題解:分塊,然后暴力維護每一塊上升序列,注意是不是最長上升序列,二分查找第二塊中大于第一塊的最后一個上升序列中的數。 注意:每一塊的大小不要用√n會T掉的,把塊的大小設為500-600都可以(T了一…

OpenBSD 5.1 正式版發布

OpenBSD 開發團隊于近日發布了 5.1 正式版。 OpenBSD是一個從NetBSD衍生出來的類Unix操作系統。項目領導人Theo de Raadt在1995年發起了OpenBSD項目,希望創造一個注重安全的操作系統,此外OpenBSD也以高品質的文件、堅持開放程式碼以及嚴格的軟件授權著名…

Spring事務傳播行為7種類型 --- 看一遍就能記住!

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 一、Spring 事務傳播行為一共有7種類型,主要分為3類: 1)支持當前事物、 2)不支持當前事…

PowerShell變量——PowerShell三分鐘(七)

有了前面的PowerShell基礎,今天我們來學習一個可以極大提升PowerShell效率的用法——變量簡答來說呢,變量就是在內存中的一個帶有名字的盒子~~~~~你可以把所有想存放的東西都放到這個“盒子”里。然后通過名字去訪問這個盒子。在訪問過程中,可…

Machine Learning - Coursera week6 Evaluating a learning algorithm

Evaluating a learning algorithm 1. Design what to do next 在預測房價的學習例子,假如你已經完成了正則化線性回歸,也就是最小化代價函數J的值。假如在你得到你的學習參數以后把它應用到放到一組新的房屋樣本上進行測試,發現在預測房價時產…

Tiny Core Linux 4.5 發布,微型 Linux 操作系統

世界上最小的Linux桌面發行版——Tiny Core Linux 今天發布了4.5版本。Tiny Core Linux是一個基于Linux2.6版本內核,采用BusyBox、Tiny X、FLTK 和其它小型軟件構筑的帶圖形用戶界面的微型Linux操作系統。由于體積很小,大約10MB,故采用整體裝…

30-- 返回倒數第 k 個節點

文章目錄1.問題描述2.代碼詳情1.問題描述 實現一種算法,找出單向鏈表中倒數第 k 個節點。返回該節點的值。 輸入: 1->2->3->4->5 和 k 2 輸出: 4 2.代碼詳情 設置快和慢兩個指針,初始化時快指針比慢指針多走k-1步…

maven的web工程打包為war并部署到服務器

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 1.在maven工程上右鍵 --> export --> 選擇WAR file --> next 2. 點擊Browse... 選擇導出后存放位置 3. 將工程名改為ROOT.war…

使用線程池的好處

第一:降低資源消耗。通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。第二:提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。第三:提高線程的可管理性。線程是稀缺資源,如果無限制的創建…

5.19 - Stacks and Queues

Decode Stringk[encoded_string] 的編碼字符串,將編碼的字符重復k次,最后打印出一個完整的字符串。 思路:使用棧結構,由里層向外層,層層解碼,當遇到了[ 字符時,向stack當中添加元素,…

Hive筆記之嚴格模式(strict mode)

Hive有一個嚴格模式,在嚴格模式下會對可能產生較大查詢結果的語句做限制,禁止其提交執行。 一、切換嚴格模式 查看當前的模式:hive> set hive.mapred.mode; hive.mapred.mode is undefined 未定義即為false,即no-strict模式。 …