回顧上一篇 神經網絡的數學基礎 :張量和梯度
通過上一篇的內容,我們知道了張量表示神經網絡中的數據,那麼數據在網絡中流動必然要經過各種運算或者叫做處理,這一系列的處理就是達到最終結果的過程。可以形象把中間的變換稱爲神經網絡的“齒輪”,或者叫做張量運算。就像二進制運算有邏輯與(AND),或(OR),異或(NOR)一樣,張量運算有以下幾種:
- 逐元素運算
- 張量點積
- 廣播
- 張量變形
1. 逐元素運算
relu運算和加法(減法)都是逐元素(element-wise)的運算,即該運算獨立地應用於張量中的每個元素。如果你想對逐元素運算編寫簡單的Python 實現,那麼可以用for循環,但是一般都使用Numpy封裝好的函數,這也是Numpy功能強大的一個原因。示例:
>>> import numpy as np
>>> x = np.array([[1,2,3],[4,5,6]])
>>> x.shape
(2, 3)
>>> y = np.array([[1,1,1],[2,2,2]])
>>> y.shape
(2, 3)
>>> z = x + y
>>> z
array([[2, 3, 4],
[6, 7, 8]])
>>> z.shape
(2, 3)
>>> z = x - y
>>> z
array([[0, 1, 2],
[2, 3, 4]])
>>> z.shape
(2, 3)
>>> z = y - x
>>> z
array([[ 0, -1, -2],
[-2, -3, -4]])
>>> z.shape
(2, 3)
2. 張量點積
點積運算,也叫張量積(tensor product,不要與逐元素的乘積弄混),是最常見也最有用的張量運算。與逐元素的運算不同,它將輸入張量的元素合併在一起。
在Numpy、Keras、Theano 和TensorFlow 中,都是用* 實現逐元素乘積。TensorFlow 中的點積使用了不同的語法,但在Numpy 和Keras 中,都是用標準的dot 運算符來實現點積。
import numpy as np
z = np.dot(x, y)
數學符號中的點(.)表示點積運算。
z=x.y
示例如下:
>>> import numpy as np
>>> x = np.array([[1,2,3],[4,5,6]])
>>> x.shape
(2, 3)
>>> y = np.array([[1,1,1],[2,2,2],[3,3,3]])
>>> y.shape
(3, 3)
>>> z = np.dot(x,y)
>>> z
array([[14, 14, 14],
[32, 32, 32]])
>>> z.shape
(2, 3)
3. 廣播
上一節所說的加減法僅支持兩個形狀相同的張量相加。如果將兩個形狀不同的張量相加,會發生什麼?如果沒有歧義的話,較小的張量會被廣播(broadcast),以匹配較大張量的形狀。廣播包含以下兩步。
(1) 向較小的張量添加軸(叫作廣播軸),使其ndim 與較大的張量相同。
(2) 將較小的張量沿着新軸重複,使其形狀與較大的張量相同。
舉例,下圖可以形象說明廣播是怎麼進行的。
>>> x = np.array([[1, 2], [3, 4]])
>>> y = np.array([10])
>>> x * y
array([[10,20],
[30,40]])
再舉一例:
>>> x = np.array([[1, 2], [3, 4]])
>>> y = np.array([10,20])
>>> x * y
array([[10,40],
[30,80]])
4. 張量變形
第四個重要的張量運算是張量變形(tensor reshaping)。張量變形是指改變張量的行和列,以得到想要的形狀。變形後的張量的元素總個數與初始
張量相同。簡單的例子可以幫助我們理解張量變形。
>>> x = np.array([[0., 1.],
[2., 3.],
[4., 5.]])
>>> print(x.shape)
(3, 2)
>>> x = x.reshape((6, 1))
>>> x
array([[ 0.],
[ 1.],
[ 2.],
[ 3.],
[ 4.],
[ 5.]])
>>> x = x.reshape((2, 3))
>>> x
array([[ 0., 1., 2.],
[ 3., 4., 5.]])
經常遇到的一種特殊的張量變形是轉置(transposition)。對矩陣做轉置是指將行和列互換,比如:使x[i, :] 變爲x[:, i]。
>>> x = np.zeros((300, 20))
>>> x = np.transpose(x)
>>> print(x.shape)
(20, 300)
小結一下,張量運算:
運算 | Numpy對應操作 |
---|---|
逐元素運算 | +,-,np.maximum |
張量點積 | np.dot |
廣播 | 操作自帶支持 |
張量變形 | reshape, np.transpose |
介紹上面的張量基本運算是爲了使學習神經網絡容易一些,至此,介紹完了基本運算。
5、神經網絡的內積
簡單來說,輸入與權重的點積就是神經網絡的內積,沒有考慮激活函數。
先看一個小例子,我們來計算一下,在神經網絡學習中可以看到如下圖所示的表達:
x代表輸入用一個1維數組表示,y代表輸出,也是一個1維數組,直線上的數字代表權重W,用2乘以3的矩陣來表示,這是一個最簡單的神經網絡,可以用下述代碼來表示表示內積:
>>> X = np.array([1, 2])
>>> X.shape
(2,)
>>> W = np.array([[1, 3, 5], [2, 4, 6]])
>>> print(W)
[[1 3 5]
[2 4 6]]
>>> W.shape
(2, 3)
>>> Y = np.dot(X, W)
>>> print(Y)
[ 5 11 17]
我們把上面的例子引伸一下,並做一些符號上的約定,,以便公式推導。
這是一個3層神經網絡結構,輸入X爲第0層,有兩個隱藏層,最後輸出算一層,共3層。
權重和隱藏層的神經元的右上角有一個“(1)”,它表示權重和神經元的層號(即第1層的權重、第1層的神經元)。此外,權重的右下角有兩個數字,它們是後一層的神經元和前一層的神經元的索引號。比如,表示前一層的第2個神經元x2 到後一層的第1個神經元的權重。權重右下角按照“後一層的索引號、前一層的索引號”的順序排列,即後前
順序,如下圖所示:
上面的神經網絡還增加了表示偏置的神經元“1”。請注意,偏置的右下角的索引號只有一個。這是因爲前一層的偏置神經元(神經元“1”)只有一個。爲了確認前面的內容,現在用數學式表示。通過加權信號和偏置的和按如下方式進行計算。
此外,如果使用矩陣的乘法運算,則可以將第1 層的加權和表示成下面的式。
其中,A(1)、X、B(1)、W(1)如下所示。
下面我們用NumPy多維數組來實現,這裏將輸入信號、權重、偏置設置成任意值。
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
print(W1.shape) # (2, 3)
print(X.shape) # (2,)
print(B1.shape) # (3,)
A1 = np.dot(X, W1) + B1
這個運算和上一節進行的運算是一樣的。W1是2 × 3 的數組,X是元素個
數爲2 的一維數組。這裏,W1和X的對應維度的元素個數也保持了一致。
至此,我們已經把神經網絡的一層的計算公式作了簡單推導,大家看到還少了激活函數,下一篇繼續在講解 激活函數 後,繼續完善上面的推導。
繼續下一篇對閱讀 激活函數的理解和實現-最新整理