張量 Tensor 是神經網路的資料表示法。
在 Python,我們常用 NumPy 陣列來作為機器學習的基礎資料結構,說 NumPy 陣列也稱為張量。
張量的維、階、軸# 階(rank): 又稱為軸(axis),代表陣列的軸數。 維(dimension): 某一階的元素個數。 1. 純量 (0D 張量)# 純量是單一個數值,也稱為 0 維張量 (0D Tensor)
2. 向量 (1D 張量)# 向量是包含單一軸的數列,也稱為 1 維張量 (1D Tensor)
3. 矩陣 (2D 張量)# 矩陣是二維的數據結構,也稱為 2 維張量 (2D Tensor)
x = [[1 , 2 , 3 ],
[4 , 5 , 6 ]]
複製 4. 3D 張量與高階張量# 3D 張量:在二維矩陣基礎上增加一個深度維度,常用於處理圖片數據 (例如 RGB 通道)。 高階張量 (nD 張量):當張量的維度超過 3D 時,用於更高維度的資料表示,例如影片、文字數據等。 x = [[[1 , 2 , 3 ],
[4 , 5 , 6 ],
[7 , 8 , 9 ]],
[[10 , 11 , 12 ],
[13 , 14 , 15 ],
[16 , 17 , 18 ]]]
複製 5. 張量的屬性# 張量擁有幾個關鍵屬性,用於描述其結構與內容:
軸 (Axes) 或 ndim 張量的軸數量,也就是維度的數量。純量的軸為 0,向量為 1,矩陣為 2,依此類推。 形狀 (Shape) 每個軸的元素數量,形狀用 tuple 表示。範例:矩陣 [[1, 2, 3], [4, 5, 6]] 的 shape 為 (2, 3)。 數據類型 (dtype)
張量中元素的資料類型,例如 float32、int32。範例:x = np.array([1, 2, 3], dtype=np.float32)。 6. batch 的概念# 在深度學習中,為了加速訓練過程,通常將多筆資料合併為一個批次 (Batch),作為張量的第一維度。例如:
一個批次包含 32 張圖片 (每張圖片為 28x28 像素):
shape = (32, 28, 28)
批次的概念允許高效處理資料,特別是在 GPU 上。
7. 張量運算# keras.layers.Dense(512, activation='relu)
的概念可以比擬成:
它是一個計算公式: output = relu(w * input + b)
w
是權重(Weights),用包學習資料間的關係。b
是偏差(Bias),用來調整輸出值。relu
是一種激活函數,它讓輸出值變得非線性。具體而言就是 (double x): double => max(0.0, x);
逐元素運算: NumPy 的運算中,很重要的就是逐元素(element-wise) 運算,意思是對張量中每個數值進行各自獨立的運算,如上述的加法與 relu 運算都是逐元素運算,非常適合平行處理,也就是向量化執行(vectorized implementations) 。
在 GPU 上執行 Tensorflow 程式碼時,會透過全面向量化的 CUDA 架構來執行逐元素運算,加快運算的效率。 張量擴張(Boardcasting): 在不考慮特例的情形,將兩個不同軸數的張量相加, NumPy 會對較小的張量進行擴張以匹配形狀較大的張量,包含:
較小的張量會加入新的軸(擴張軸)以匹配較大的張量。 較小的張量會在這些新的軸上重複寫入元素,以匹配較大張量的形狀。 import numpy as np
x = np. array((1 ,2 ,3 ,4 ,5 ))
y = np. array((1 ))
z = x + y
z
>>> array([2 ,3 ,4 ,5 ,6 ])
複製 y = np. array((1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ))
>>> array([1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ])
y1 = np. expand_dims(y, axis= 0 )
>>> array([[1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ]])
y2 = np. concatenate([y1]* 2 , axios= 0 )
>>> array([[1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ],
[1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ]])
複製 張量的點積運算
在 NumPy 中會使用 np.dot
函式來進行點積運算 向量 x 與向量 y 做點積,在一般邏輯上可表示成: def naive_vector_dot (x, y):
assert len(x. shape) == 1
assert len(y. shape) == 1
assert x. shape[0 ] == y. shape[0 ]
z = 0.
for i in range(x. shape[0 ]):
z += x[i] * y[i]
return z
複製 矩陣 x 與向量 y 做點積,在一般邏輯上可表示成: def naive_matrix_vector_dot (x, y):
assert len(x. shape) == 2
assert len(y. shape) == 1
assert x. shape[1 ] == y. shape[0 ]
z = np. zeros(x. shape[0 ])
for i in range(x. shape[0 ]):
for j in range(x. shape[1 ]):
z[i] += x[i, j] * y[j]
return z
複製 張量重塑
重塑就是調整張量各軸內的元素數,而張量元素總數不變的一種手法。 如在前一回使用過的資料前處理 train_images = train_images.reshap((60000, 28*28))
就是一種重塑 常見的重塑還有矩陣轉置(transposition) ,其實就是 x[i, :] => x[:, i]
張量運算的幾何解釋
1. 平移(Translation)# 平移是一種將空間中的點沿特定方向移動的操作。 在幾何上,平移不是線性運算,需要用齊次座標 表示。 假設平移向量為 t = [ t x t y ] \mathbf{t} = \begin{bmatrix} t_x \\ t_y \end{bmatrix} t = [ t x t y ] ,則點 p = [ x y ] \mathbf{p} = \begin{bmatrix} x \\ y \end{bmatrix} p = [ x y ] 被平移後的位置為:
p ’ = p + t = [ x y ] + [ t x t y ] = [ x + t x y + t y ]
\mathbf{p}’ = \mathbf{p} + \mathbf{t} =
\begin{bmatrix}
x\\
y
\end{bmatrix}
+
\begin{bmatrix}
t_x\\
t_y
\end{bmatrix}
=
\begin{bmatrix}
x + t_x \\
y + t_y
\end{bmatrix}
p ’ = p + t = [ x y ] + [ t x t y ] = [ x + t x y + t y ]
在齊次座標中,可以用矩陣形式表示為:
[ x ’ y ’ 1 ] = [ 1 0 t x 0 1 t y 0 0 1 ] . [ x y 1 ]
\begin{bmatrix}
x’ \\
y’ \\
1
\end{bmatrix}
=
\begin{bmatrix}
1 & 0 & t_x \\
0 & 1 & t_y \\
0 & 0 & 1
\end{bmatrix}
.
\begin{bmatrix}
x \\
y \\
1
\end{bmatrix}
⎣ ⎡ x ’ y ’ 1 ⎦ ⎤ = ⎣ ⎡ 1 0 0 0 1 0 t x t y 1 ⎦ ⎤ . ⎣ ⎡ x y 1 ⎦ ⎤
2. 旋轉 (Rotation)# 旋轉是將點繞某個固定點(通常是原點)旋轉一個角度的操作。 旋轉矩陣(以逆時針旋轉角度 θ \theta θ ) 為:
R = [ cos θ − sin θ sin θ cos θ ]
\mathbf{R} =
\begin{bmatrix}
\cos\theta & -\sin\theta \\
\sin\theta & \cos\theta
\end{bmatrix}
R = [ cos θ sin θ − sin θ cos θ ]
對於
對於點 p = [ x y ] \mathbf{p} = \begin{bmatrix} x \\ y \end{bmatrix} p = [ x y ] ,旋轉後的位置為:
p ’ = R ⋅ p = [ cos θ − sin θ sin θ cos θ ] . [ x y ]
\mathbf{p}’ = \mathbf{R} \cdot \mathbf{p} =
\begin{bmatrix}
\cos\theta & -\sin\theta \\
\sin\theta & \cos\theta
\end{bmatrix}
.
\begin{bmatrix}
x \\
y
\end{bmatrix}
p ’ = R ⋅ p = [ cos θ sin θ − sin θ cos θ ] . [ x y ]
在齊次座標中表示為:
[ x ’ y ’ 1 ] = [ cos θ − sin θ 0 sin θ cos θ 0 0 0 1 ] . [ x y 1 ]
\begin{bmatrix}
x’ \\
y’ \\
1
\end{bmatrix}
=
\begin{bmatrix}
\cos\theta & -\sin\theta & 0 \\
\sin\theta & \cos\theta & 0 \\
0 & 0 & 1
\end{bmatrix}
.
\begin{bmatrix}
x \\
y \\
1
\end{bmatrix}
⎣ ⎡ x ’ y ’ 1 ⎦ ⎤ = ⎣ ⎡ cos θ sin θ 0 − sin θ cos θ 0 0 0 1 ⎦ ⎤ . ⎣ ⎡ x y 1 ⎦ ⎤
3. 縮放 (Scaling)# 縮放改變點的大小,可以分別對 x x x 和 y y y 軸進行不同比例的縮放。
縮放矩陣為:
S = [ s x 0 0 s y ]
\mathbf{S} =
\begin{bmatrix}
s_x & 0 \\
0 & s_y
\end{bmatrix}
S = [ s x 0 0 s y ]
對於點 p = [ x y ] \mathbf{p} = \begin{bmatrix} x \\ y \end{bmatrix} p = [ x y ] ,縮放後的位置為:p ’ = S ⋅ p = [ s x 0 0 s y ] . [ x y ]
\mathbf{p}’ = \mathbf{S} \cdot \mathbf{p} =
\begin{bmatrix}
s_x & 0 \\
0 & s_y
\end{bmatrix}
.
\begin{bmatrix}
x \\
y
\end{bmatrix}
p ’ = S ⋅ p = [ s x 0 0 s y ] . [ x y ]
在齊次座標中表示為:
[ x ’ y ’ 1 ] = [ s x 0 0 0 s y 0 0 0 1 ] . [ x y 1 ]
\begin{bmatrix}
x’ \\
y’ \\
1
\end{bmatrix}
=
\begin{bmatrix}
s_x & 0 & 0 \\
0 & s_y & 0 \\
0 & 0 & 1
\end{bmatrix}
.
\begin{bmatrix}
x \\
y \\
1
\end{bmatrix}
⎣ ⎡ x ’ y ’ 1 ⎦ ⎤ = ⎣ ⎡ s x 0 0 0 s y 0 0 0 1 ⎦ ⎤ . ⎣ ⎡ x y 1 ⎦ ⎤
線性變換是縮放、旋轉或剪切等操作的統稱,可以用矩陣表示。 一般的線性變換矩陣為:A = [ a 11 a 12 a 21 a 22 ]
\mathbf{A} =
\begin{bmatrix}
a_{11} & a_{12} \\
a_{21} & a_{22}
\end{bmatrix}
A = [ a 11 a 21 a 12 a 22 ]
對於點 p = [ x y ] \mathbf{p} = \begin{bmatrix} x \\ y \end{bmatrix} p = [ x y ] ,線性變換後的位置為:p ’ = A ⋅ p = [ a 11 a 12 a 21 a 22 ] . [ x y ]
\mathbf{p}’ = \mathbf{A} \cdot \mathbf{p} =
\begin{bmatrix}
a_{11} & a_{12} \\
a_{21} & a_{22}
\end{bmatrix}
.
\begin{bmatrix}
x \\
y
\end{bmatrix}
p ’ = A ⋅ p = [ a 11 a 21 a 12 a 22 ] . [ x y ]
在齊次座標中:
[ x ’ y ’ 1 ] = [ a 11 a 12 0 a 21 a 22 0 0 0 1 ] . [ x y 1 ]
\begin{bmatrix}
x’ \\
y’ \\
1
\end{bmatrix}
=
\begin{bmatrix}
a_{11} & a_{12} & 0 \\
a_{21} & a_{22} & 0 \\
0 & 0 & 1
\end{bmatrix}
.
\begin{bmatrix}
x \\
y \\
1
\end{bmatrix}
⎣ ⎡ x ’ y ’ 1 ⎦ ⎤ = ⎣ ⎡ a 11 a 21 0 a 12 a 22 0 0 0 1 ⎦ ⎤ . ⎣ ⎡ x y 1 ⎦ ⎤
仿射變換是線性變換加上平移的組合。 在齊次座標中,仿射變換可表示為:T = [ a 11 a 12 t x a 21 a 22 t y 0 0 1 ]
\mathbf{T} =
\begin{bmatrix}
a_{11} & a_{12} & t_x \\
a_{21} & a_{22} & t_y \\
0 & 0 & 1
\end{bmatrix}
T = ⎣ ⎡ a 11 a 21 0 a 12 a 22 0 t x t y 1 ⎦ ⎤
對於點 p = [ x y ] \mathbf{p} = \begin{bmatrix} x \\ y \end{bmatrix} p = [ x y ] ,仿射變換後的位置為:
[ x ’ y ’ 1 ] = [ a 11 a 12 t x a 21 a 22 t y 0 0 1 ] . [ x y 1 ]
\begin{bmatrix}
x’ \\
y’ \\
1
\end{bmatrix}
=
\begin{bmatrix}
a_{11} & a_{12} & t_x \\
a_{21} & a_{22} & t_y \\
0 & 0 & 1
\end{bmatrix}
.
\begin{bmatrix}
x \\
y \\
1
\end{bmatrix}
⎣ ⎡ x ’ y ’ 1 ⎦ ⎤ = ⎣ ⎡ a 11 a 21 0 a 12 a 22 0 t x t y 1 ⎦ ⎤ . ⎣ ⎡ x y 1 ⎦ ⎤
由於仿射是線性變換(矩陣的點積)與平移運算(向量加法)的結合,其實就是密集層會用到的 y = W ⋅ x + b \mathbf{y}=\mathbf{W}\cdot\mathbf{x}+\mathbf{b} y = W ⋅ x + b 。一個沒有激活函數的密集層就是仿射層。 換句話說,在沒有激活函數的狀況下進行了無數次的仿射變換,可以等同於一次仿射變換
y 1 = W 1 ⋅ x + b 1 y 2 = W 2 ⋅ y 1 + b 2
\begin{align*}
y_1 &= W_1 \cdot x + b_1 \\
y_2 &= W_2 \cdot y_1 + b_2
\end{align*}
y 1 y 2 = W 1 ⋅ x + b 1 = W 2 ⋅ y 1 + b 2
將 y 1 y_1 y 1 代入 y 2 y_2 y 2 的公式:
y 2 = W 2 ⋅ ( W 1 ⋅ x + b 1 ) + b 2 y 2 = W 2 ⋅ W 1 ⋅ x + W 2 ⋅ b 1 + b 2
\begin{align*}
y_2 &= W_2 \cdot (W_1 \cdot x + b_1) + b_2 \\
y_2 &= W_2 \cdot W_1 \cdot x + W_2 \cdot b_1 + b_2
\end{align*}
y 2 y 2 = W 2 ⋅ ( W 1 ⋅ x + b 1 ) + b 2 = W 2 ⋅ W 1 ⋅ x + W 2 ⋅ b 1 + b 2
設 W = W 2 ⋅ W 1 W = W_2 \cdot W_1 W = W 2 ⋅ W 1 和 b = W 2 ⋅ b 1 + b 2 b = W_2 \cdot b_1 + b_2 b = W 2 ⋅ b 1 + b 2 ,則:
y 2 = W ⋅ x + b
y_2 = W \cdot x + b
y 2 = W ⋅ x + b 這個結論非常重要,代表:如果我們建構了多個密集層的神經網路,卻沒有搭配任何的激活函數,其效果等同於一個密集層,換言之,這個「深層」的神經網路模型不過是一個線性模型。