官术网_书友最值得收藏!

1.2 神經網絡的推理

現在我們開始復習神經網絡。神經網絡中進行的處理可以分為學習和推理兩部分。本節將圍繞神經網絡的推理展開說明,而神經網絡的學習會在下一節進行討論。

1.2.1 神經網絡的推理的全貌圖

簡單地說,神經網絡就是一個函數。函數是將某些輸入變換為某些輸出的變換器,與此相同,神經網絡也將輸入變換為輸出。

舉個例子,我們來考慮輸入二維數據、輸出三維數據的函數。為了使用神經網絡進行實現,需要在輸入層準備2個神經元,在輸出層準備3個神經元。然后,在隱藏層(中間層)放置若干神經元,這里我們放置4個神經元。

這樣一來,我們的神經網絡就可以畫成圖1-7。

圖1-7 神經網絡的例子

在圖1-7中,用〇表示神經元,用箭頭表示它們的連接。此時,在箭頭上有權重,這個權重和對應的神經元的值分別相乘,其和(嚴格地講,是經過激活函數變換后的值)作為下一個神經元的輸入。另外,此時還要加上一個不受前一層的神經元影響的常數,這個常數稱為偏置。因為所有相鄰的神經元之間都存在由箭頭表示的連接,所以圖1-7的神經網絡稱為全連接網絡

圖1-7的網絡一共包含3層,但有權重的層實際上是2層,本書中將這樣的神經網絡稱為2層神經網絡。因為圖1-7的網絡由3層組成,所以有的文獻也稱之為3層神經網絡。

下面用數學式來表示圖1-7的神經網絡進行的計算。這里用(x1, x2)表示輸入層的數據,用w11w12表示權重,用b1表示偏置。這樣一來,圖1-7中的隱藏層的第1個神經元就可以如下進行計算:

如式(1.2)所示,隱藏層的神經元是基于加權和計算出來的。之后,改變權重和偏置的值,根據神經元的個數,重復進行相應次數的式(1.2)的計算,這樣就可以求出所有隱藏層神經元的值。

權重和偏置都有下標,這個下標的規則(為何將下標設為11或12等)并不是很重要,重要的是神經元是通過加權和計算的,并且可以通過矩陣乘積整體計算。實際上,基于全連接層的變換可以通過矩陣乘積如下進行整理:

這里,隱藏層的神經元被整理為(h1, h2, h3, h4),它可以看作1×4的矩陣(或者行向量)。另外,輸入是(x1, x2),這是一個1×2的矩陣。再者,權重是2×4的矩陣,偏置是1×4的矩陣。這樣一來,式(1.3)可以如下進行簡化:

這里,輸入是x,隱藏層的神經元是h,權重是W,偏置是b,這些都是矩陣。此時,留意式(1.4)的矩陣形狀,可知進行了如圖1-8所示的變換。

圖1-8 形狀檢查:確認對應維度的元素個數一致(省略偏置)

如圖1-8所示,在矩陣乘積中,要使對應維度的元素個數一致。通過像這樣觀察矩陣的形狀,可以確認變換是否正確。

在矩陣乘積的計算中,形狀檢查非常重要。據此,可以判斷計算是否正確(至少可以判斷計算是否成立)。

這樣一來,我們就可以利用矩陣來整體計算全連接層的變換。不過,這里進行的變換只針對單筆樣本數據(輸入數據)。在神經網絡領域,我們會同時對多筆樣本數據(稱為mini-batch,小批量)進行推理和學習。因此,我們將單獨的樣本數據保存在矩陣x的各行中。假設要將N筆樣本數據作為mini-batch整體處理,關注矩陣的形狀,其變換如圖1-9所示。

圖1-9 形狀檢查:mini-batch版的矩陣乘積(省略偏置)

如圖1-9所示,根據形狀檢查,可知各mini-batch被正確地進行了變換。此時,N筆樣本數據整體由全連接層進行變換,隱藏層的N個神經元被整體計算出來。現在,我們用Python寫出mini-batch版的全連接層變換。

        >>> import numpy as np
        >>> W1 = np.random.randn(2 , 4) # 權重
        >>> b1 = np.random.randn(4)     # 偏置
        >>> x = np.random.randn(10 , 2) # 輸入
        >>> h = np.dot(x , W1) + b1

在這個例子中,10筆樣本數據分別由全連接層進行變換。此時,x的第1個維度對應于各筆樣本數據。比如,x[0]是第0筆輸入數據,x[1]是第1筆輸入數據……類似地,h[0]是第0筆數據的隱藏層的神經元,h[1]是第1筆數據的隱藏層的神經元,以此類推。

在上面的代碼中,偏置b1的加法運算會觸發廣播功能。b1的形狀是(4,),它會被自動復制,變成(10, 4)的形狀。

全連接層的變換是線性變換。激活函數賦予它“非線性”的效果。嚴格地講,使用非線性的激活函數,可以增強神經網絡的表現力。激活函數有很多種,這里我們使用式(1.5)的sigmoid函數(sigmoid function):

如圖1-10所示,sigmoid函數呈S形曲線。

圖1-10 sigmoid函數的圖像

sigmoid函數接收任意大小的實數,輸出0~1的實數。現在我們用Python來實現這個sigmoid函數。

        def sigmoid(x):
            return 1 / (1 + np.exp(-x))

這是式(1.5)的直接實現,應該沒有特別難的地方。現在,我們使用這個sigmoid函數,來變換剛才的隱藏層的神經元。

        >>> a = sigmoid(h)

基于sigmoid函數,可以進行非線性變換。然后,再用另一個全連接層來變換這個激活函數的輸出a(也稱為activation)。這里,因為隱藏層有4個神經元,輸出層有3個神經元,所以全連接層使用的權重矩陣的形狀必須設置為4×3,這樣就可以獲得輸出層的神經元。以上就是神經網絡的推理。現在我們用Python將這一段內容總結如下。

        import numpy as np
        def sigmoid(x):
            return 1 / (1 + np.exp(-x))
        x = np.random.randn(10, 2)
        W1 = np.random.randn(2, 4)
        b1 = np.random.randn(4)
        W2 = np.random.randn(4, 3)
        b2 = np.random.randn(3)
        h = np.dot(x, W1) + b1
        a = sigmoid(h)
        s = np.dot(a, W2) + b2

這里,x的形狀是(10, 2),表示10筆二維數據組織為了1個mini-batch。最終輸出的s的形狀是(10, 3)。同樣,這意味著10筆數據一起被處理了,每筆數據都被變換為了三維數據。

上面的神經網絡輸出了三維數據。因此,使用各個維度的值,可以分為3個類別。在這種情況下,輸出的三維向量的各個維度對應于各個類的“得分”(第1個神經元是第1個類別,第2個神經元是第2個類別……)。在實際進行分類時,尋找輸出層神經元的最大值,將與該神經元對應的類別作為結果。

得分是計算概率之前的值。得分越高,這個神經元對應的類別的概率也越高。后面我們會看到,通過把得分輸入Softmax函數,可以獲得概率。

以上就是神經網絡的推理部分的實現。接下來,我們使用Python的類,將這些處理實現為層。

1.2.2 層的類化及正向傳播的實現

現在,我們將神經網絡進行的處理實現為層。這里將全連接層的變換實現為Affine層,將sigmoid函數的變換實現為Sigmoid層。因為全連接層的變換相當于幾何學領域的仿射變換,所以稱為Affine層。另外,將各個層實現為Python的類,將主要的變換實現為類的forward()方法。

神經網絡的推理所進行的處理相當于神經網絡的正向傳播。顧名思義,正向傳播是從輸入層到輸出層的傳播。此時,構成神經網絡的各層從輸入向輸出方向按順序傳播處理結果。之后我們會進行神經網絡的學習,那時會按與正向傳播相反的順序傳播數據(梯度),所以稱為反向傳播

神經網絡中有各種各樣的層,我們將其實現為Python的類。通過這種模塊化,可以像搭建樂高積木一樣構建網絡。本書在實現這些層時,制定以下“代碼規范”。

·所有的層都有forward()方法和backward()方法

·所有的層都有params和grads實例變量

簡單說明一下這個代碼規范。首先,forward()方法和backward()方法分別對應正向傳播和反向傳播。其次,params使用列表保存權重和偏置等參數(參數可能有多個,所以用列表保存)。grads以與params中的參數對應的形式,使用列表保存各個參數的梯度(后述)。這就是本書的代碼規范。

遵循上述代碼規范,代碼看上去會更清晰。我們后面會說明為什么要遵循這樣的規范,以及它的有效性。

因為這里只考慮正向傳播,所以我們僅關注代碼規范中的以下兩點:一是在層中實現forward()方法;二是將參數整理到實例變量params中。我們基于這樣的代碼規范來實現層,首先實現Sigmoid層,如下所示(ch01/forward_net.py)。

        import numpy as np

            class Sigmoid:
            def__init__(self):
                self.params = []

            def forward(self, x):
                return 1 / (1 + np.exp(-x))

如上所示,sigmoid函數被實現為一個類,主變換處理被實現為forward(x)方法。這里,因為Sigmoid層沒有需要學習的參數,所以使用空列表來初始化實例變量params。下面,我們接著來看一下全連接層Affine層的實現,如下所示(ch01/forward_net.py)。

        class Affine:
            def__init__(self, W, b):
                self.params = [W, b]

            def forward(self, x):
                W, b = self.params
                out = np.dot(x, W) + b
                return out

Affine層在初始化時接收權重和偏置。此時,Affine層的參數是權重和偏置(在神經網絡的學習時,這兩個參數隨時被更新)。因此,我們使用列表將這兩個參數保存在實例變量params中。然后,實現基于forward(x)的正向傳播的處理。

根據本書的代碼規范,所有的層都需要在實例變量params中保存要學習的參數。因此,可以很方便地將神經網絡的全部參數整理在一起,參數的更新操作、在文件中保存參數的操作都會變得更容易。

現在,我們使用上面實現的層來實現神經網絡的推理處理。這里實現如圖1-11所示的層結構的神經網絡。

圖1-11 要實現的神經網絡的層結構

如圖1-11所示,輸入X經由Affine層、Sigmoid層和Affine層后輸出得分S。我們將這個神經網絡實現為名為TwoLayerNet的類,將主推理處理實現為predict(x)方法。

之前,我們在用圖表示神經網絡時,使用的是像圖1-7那樣的“神經元視角”的圖。與此相對,圖1-11是“層視角”的圖。

TwoLayerNet的代碼如下所示(ch01/forward_net.py)。

        class TwoLayerNet:
            def__init__(self, input_size, hidden_size, output_size):
                I, H, O = input_size, hidden_size, output_size

                # 初始化權重和偏置
                W1 = np.random.randn(I, H)
                b1 = np.random.randn(H)
                W2 = np.random.randn(H, O)
                b2 = np.random.randn(O)

                  # 生成層
                  self.layers = [
                      Affine(W1, b1),
                      Sigmoid(),
                      Affine(W2, b2)
                  ]

                  # 將所有的權重整理到列表中
                  self.params = []
                  for layer in self.layers:
                      self.params += layer.params

              def predict(self, x):
                  for layer in self.layers:
                      x = layer.forward(x)
                  return x

在這個類的初始化方法中,首先對權重進行初始化,生成3個層。然后,將要學習的權重參數一起保存在params列表中。這里,因為各個層的實例變量params中都保存了學習參數,所以只需要將它們拼接起來即可。這樣一來,TwoLayerNet的params變量中就保存了所有的學習參數。像這樣,通過將參數整理到一個列表中,可以很輕松地進行參數的更新和保存。

此外,Python中可以使用+運算符進行列表之間的拼接。下面是一個簡單的例子。

        >>> a = ['A' , 'B']
        >>> a += ['C' , 'D']
        >>> a
        ['A', 'B', 'C', 'D']

如上所示,通過列表之間的加法將列表拼接了起來。在上面的TwoLayerNet的實現中,通過將各個層的params列表加起來,從而將全部學習參數整理到了一個列表中。現在,我們使用TwoLayerNet類進行神經網絡的推理。

        x = np.random.randn(10, 2)
        model = TwoLayerNet(2, 4, 3)
        s = model.predict(x)

這樣就可以求出輸入數據x的得分s了。像這樣,通過將層實現為類,可以輕松實現神經網絡。另外,因為要學習的參數被匯總在model.params中,所以之后進行神經網絡的學習會更加容易。

主站蜘蛛池模板: 原阳县| 绥棱县| 瓮安县| 准格尔旗| 尚义县| 临夏县| 四平市| 日照市| 肥东县| 临泽县| 高州市| 慈溪市| 甘孜县| 崇阳县| 江门市| 紫阳县| 师宗县| 东城区| 玛曲县| 定边县| 凤城市| 博客| 保定市| 平定县| 嘉黎县| 祁门县| 墨江| 聂荣县| 玉田县| 南宫市| 新巴尔虎左旗| 龙陵县| 区。| 三江| 崇阳县| 获嘉县| 晋宁县| 北安市| 文化| 徐州市| 泰和县|