《Neural Networks and Deep Learning》讀書筆記:最簡單的識別MNIST的神經網絡程序(2)

轉載請註明出處:https://www.codelast.com/

本文是上一篇文章的續文。
Neural Networks and Deep Learning》一書的中文譯名是《神經網絡與深度學習》,書如其名,不需要解釋也知道它是講什麼的,這是本入門級的好書。
在第一章中,作者展示瞭如何編寫一個簡單的、用於識別MNIST數據的Python神經網絡程序。
本文接着上一篇文章對程序代碼進行解析。

下面來看看 SGD() 方法的實現。先把它的完整代碼貼上來:

def SGD(self, training_data, epochs, mini_batch_size, eta,
test_data=None):
"""Train the neural network using mini-batch stochastic
gradient descent.  The ``training_data`` is a list of tuples
``(x, y)`` representing the training inputs and the desired
outputs.  The other non-optional parameters are
self-explanatory.  If ``test_data`` is provided then the
network will be evaluated against the test data after each
epoch, and partial progress printed out.  This is useful for
tracking progress, but slows things down substantially."""
if test_data: n_test = len(test_data)
n = len(training_data)
for j in xrange(epochs):
random.shuffle(training_data)
mini_batches = [
training_data[k:k + mini_batch_size]
for k in xrange(0, n, mini_batch_size)]
for mini_batch in mini_batches:
self.update_mini_batch(mini_batch, eta)
if test_data:
print "Epoch {0}: {1} / {2}".format(
j, self.evaluate(test_data), n_test)
else:
print "Epoch {0} complete".format(j)

代碼自帶詳細註釋,而且很容易看懂。
文章來源:https://www.codelast.com/
for j in xrange(epochs) 這句代碼使得訓練會進行epoch輪。
xrang()是Python自帶函數,隨便試驗一下就知道它的作用了,例如:

for j in xrange(4):
print(j)

這段代碼輸出的結果是:

0
1
2
3

所以,如果我們把epoch定義成4,那麼循環就會進行4次,也就是說訓練會進行4輪。
文章來源:https://www.codelast.com/
顯然,for j in xrange(epochs) 下面的循環體裏的代碼,就是每一輪訓練要執行的代碼。
首先,random.shuffle(training_data)這一句的作用是什麼呢?答:隨機打亂training_data這個list。
爲了說明它,我們打開ipython,來做一個相當簡單的試驗:

import numpy as np
import random
a = [(1, 2), (3, 4), (5, 6), (7, 8), (0, 9)]
random.shuffle(a)
print(a)
random.shuffle(a)
print(a)
random.shuffle(a)
print(a)

在上面的代碼中,打印了3次 a 的內容,在我的PC上,3次print的輸出分別如下:

[(7, 8), (0, 9), (1, 2), (3, 4), (5, 6)]
[(3, 4), (5, 6), (0, 9), (7, 8), (1, 2)]
[(1, 2), (0, 9), (5, 6), (3, 4), (7, 8)]

可見,random.shuffle()把list裏的元素進行了隨機打亂。由於是隨機的,所以,你的測試結果可能和我的不一樣。
隨機打亂數據是爲了防止某些pattern相似的輸入數據集中在一個batch中,導致對訓練結果產生負面影響。SGD不就是“隨機梯度下降”嘛。
文章來源:https://www.codelast.com/
在打亂數據之後,程序就把所有輸入數據拆分成了若干個批量(mini batch),每一個batch的大小是由mini_batch_size定義的:

mini_batches = [
training_data[k:k+mini_batch_size]
for k in xrange(0, n, mini_batch_size)]

這裏的xrange用法和上面的xrange稍有不同,我們還是用一個實例來表明它的作用:

for k in xrange(0, 10, 3):
print(k)

這段代碼把mini_batch_size設置成了3,它的輸出結果是:

0
3
6
9
可見,它會使k從0開始,按mini_batch_size的大小爲步長遞增,但最大值不超過第二個參數。
所以,training_data也是按這個套路實現了分割。
文章來源:https://www.codelast.com/
在training_data分割得到了若干個mini batch之後,下面就是對每一個mini batch分別進行訓練,從而求得參數向量 w 和 b 的值。但這裏是一個串行的計算,也就是說第一個mini batch計算完了,才輪到第二個mini batch計算,依此類推。
對每一個mini batch求解參數向量,其實就是這一句代碼調用(eta即 η ,學習率,這是一個人爲設定其值的超參數):

self.update_mini_batch(mini_batch, eta)

有人可能會說,這跟待求的參數向量 w 和 b 沒什麼關係啊?在上一篇文章中我們看到, w 和 b 被定義成了Network類的成員變量,update_mini_batch()其實是在函數體內計算出、並更新了它們的值,所以只是表面上看起來“沒關係”,實際上完全有關係。

在循環迭代完所有的mini batch之後, w 和 b 的值也就被更新完了,即“學習”的過程也就結束了。所以,隨着迭代的進行, w 和 b 的值越來越接近理想值,所有迭代結束之後,我們就認爲 w 和 b 的值已經達到了理想值。
文章來源:https://www.codelast.com/
在每一輪迭代的最後,有下面這段代碼:

if test_data:
print "Epoch {0}: {1} / {2}".format(j, self.evaluate(test_data), n_test)
else:
print "Epoch {0} complete".format(j)

在每一輪迭代更新完 w 和 b 的值之後,如果test_data不爲空的話,那麼就會用evaluate()方法對本輪的計算結果進行評估。理論上,隨着迭代一輪一輪的進行,評估結果應該越來越好。
文章來源:https://www.codelast.com/
所以,現在最關鍵的代碼就封裝在了update_mini_batch()方法中。這個方法是怎麼對 w 和 b 進行計算的呢?且聽下回分解。

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