In [1]:
import numpy as np
In [2]:
%run magic.ipynb
我們的輸入 $x=\begin{pmatrix} x_0 \\ x_1 \\ x_2 \\ x_3 \end{pmatrix} $ 是一個向量,我們看成 column vector 好了
而 Weight: $W = \begin{pmatrix} W_0 \\ W_1 \\ W_2 \end{pmatrix} = \begin{pmatrix} W_{0,0} & W_{0,1} & W_{0,2} & W_{0,3}\\ W_{1,0} & W_{1,1} & W_{1,2} & W_{1,3} \\ W_{2,0} & W_{2,1} & W_{2,2} & W_{2,3} \end{pmatrix} $
Bias: $b=\begin{pmatrix} b_0 \\ b_1 \\ b_2 \end{pmatrix} $
我們先計算"線性輸出" $ c = \begin{pmatrix} c_0 \\ c_1 \\ c_2 \end{pmatrix} = Wx+b = \begin{pmatrix} W_0 x + b_0 \\ W_1 x + b_1 \\ W_2 x + b_2 \end{pmatrix} $, 然後再取 $exp$ (逐項取)。 最後得到一個向量。
$d = \begin{pmatrix} d_0 \\ d_1 \\ d_2 \end{pmatrix} = e^{W x + b} = \begin{pmatrix} e^{c_0} \\ e^{c_1} \\ e^{c_2} \end{pmatrix}$
將這些數值除以他們的總和。 我們希望出來的數字會符合這張圖片是這個數字的條件機率。
先隨便算一個 $\mathbb{R}^2 \rightarrow \mathbb{R}^3$ 的網路
In [3]:
# Weight
W = Matrix([1,2],[3,4], [5,6])
W
Out[3]:
In [4]:
# Bias
b = Vector(1,0,-1)
b
Out[4]:
In [5]:
# 輸入
x = Vector(2,-1)
x
Out[5]:
In [ ]:
# 參考答案
# Wx+b
c = W @ x + b
# d = exp(Wx+b)
d = np.exp(c)
# q = d/sum(d)
q = d/d.sum()
In [9]:
# Hint 下面產生數字 i 的 2 進位向量
i = 13
x = Vector(i%2, (i>>1)%2, (i>>2)%2, (i>>3)%2)
x
Out[9]:
In [ ]:
# 參考答案
W = Matrix([-1,-1,0,0], [1,-1,0,0], [-1,1,0,0], [1,1,0,0])
b = Vector(0,0,0,0)
for i in range(16):
x = Vector(i%2, (i>>1)%2, (i>>2)%2, (i>>3)%2)
r = W @ x + b
print("i=", i, "predict:", r.argmax(), "ground truth:", i%4)
In [ ]:
# 參考答案
W = Matrix([0,0,0,0], [1,-1,1,-1], [-1,1,-1,1])
b = Vector(0.1,0,0)
for i in range(16):
x = Vector(i%2, (i>>1)%2, (i>>2)%2, (i>>3)%2)
r = W @ x + b
print("i=", i, "predict:", r.argmax(), "ground truth:", i%3)
我們用一種被稱作是 gradient descent 的方式來改善我們的誤差。
因為我們知道 gradient 是讓函數上升最快的方向。所以我們如果朝 gradient 的反方向走一點點(也就是下降最快的方向),那麼得到的函數值應該會小一點。
記得我們的變數是 $W$ 和 $b$ (裡面有一堆 W_i,j b_i 這些變數),所以我們要把 $loss$ 對 $W$ 和 $b$ 裡面的每一個參數來偏微分。
還好這個偏微分是可以用手算出他的形式,而最後偏微分的式子也不會很複雜。
$loss$ 展開後可以寫成
= \log(\sum_j e^{W_j x + b_j}) - W_i x - b_i$
注意 $d_j = e^{W_j x + b_j}$ 只有變數 $b_j, W_j$
對 $k \neq i$ 時, $loss$ 對 $b_k$ 的偏微分是 $$ \frac{e^{W_k x + b_k}}{\sum_j e^{W_j x + b_j}} = q(k)$$ 對 $k = i$ 時, $loss$ 對 $b_k$ 的偏微分是 $$ q(k) - 1$$
對 $W$ 的偏微分也不難
對 $k \neq i$ 時, $loss$ 對 $W_{k,t}$ 的偏微分是 $$ \frac{e^{W_k x + b_k} W_{k,t} x_t}{\sum_j e^{W_j x + b_j}} = q(k) x_t$$ 對 $k = i$ 時, $loss$ 對 $W_{k,t}$ 的偏微分是 $$ q(k) x_t - x_t$$
In [14]:
# 先產生隨機的 W 和 b
W = Matrix(np.random.normal(size=(3,4)))
b = Vector(np.random.normal(size=(3,)))
In [15]:
W
Out[15]:
In [16]:
b
Out[16]:
In [17]:
i = 14
x = Vector(i%2, (i>>1)%2, (i>>2)%2, (i>>3)%2)
y = i%3
In [ ]:
# 參考答案
# Wx+b
c = W @ x + b
# d = exp(Wx+b)
d = np.exp(c)
# q = d/sum(d)
q = d/d.sum()
In [ ]:
# 參考答案
loss = -np.log(q[y])
loss
In [23]:
#參考答案
%run -i softmax_compute_grad_b.py
grad_b
Out[23]:
In [ ]:
# 參考答案
grad_W = q @ x.T
grad_W[y] -= x.ravel()
# or
grad_W = grad_b @ x.T
grad_W
In [27]:
# 參考答案
%run -i softmax_update_Wb.py
In [28]:
# 原先的 q
q
Out[28]:
In [29]:
# 原先的 loss
loss
Out[29]:
In [30]:
# 現在的 loss
%run -i softmax_compute_q.py
%run -i softmax_compute_loss1.py
loss
Out[30]:
In [31]:
q
Out[31]:
In [32]:
X = np.array([Vector(i%2, (i>>1)%2, (i>>2)%2, (i>>3)%2) for i in range(16)])
for i in range(4):
print("i=", i)
display(X[i])
In [33]:
X
Out[33]:
In [34]:
# 對應的組別
y = np.array([i%3 for i in range(16)])
y
Out[34]:
In [35]:
# 請在這裡計算
# 參考解答如後
對照
d = np.exp(W @ x + b)
q = d/d.sum()
q
In [36]:
d = np.exp(W @ X + b)
q = d/d.sum(axis=(1,2), keepdims=True)
q
Out[36]:
對照
loss = -np.log(q[y])
loss
In [37]:
loss = -np.log(q[range(len(y)), y])
loss
Out[37]:
In [38]:
# 用平均當成我們真正的 loss
loss.mean()
Out[38]:
對照
grad_b = q - np.eye(3)[y][:, None]
In [39]:
# fancy indexing :p
one_y = np.eye(3)[y][..., None]
grad_b_all = q - one_y
grad_b = grad_b_all.mean(axis=0)
grad_b
Out[39]:
對照
grad_W = grad_b @ x.T
In [40]:
grad_W_all = grad_b_all @ X.swapaxes(1,2)
grad_W = grad_W_all.mean(axis=0)
grad_W
Out[40]:
In [41]:
W -= 0.5 * grad_W
b -= 0.5 * grad_b
In [42]:
# 之前的 loss
loss.mean()
Out[42]:
In [43]:
d = np.exp(W @ X + b)
q = d/d.sum(axis=(1,2), keepdims=True)
loss = -np.log(q[range(len(y)), y])
loss.mean()
Out[43]:
In [44]:
# 在這裡計算
In [45]:
# 參考答案
%run -i softmax_train.py
In [46]:
# 畫出 loss 的曲線
%matplotlib inline
import matplotlib.pyplot as plt
plt.plot(loss_history);
In [47]:
# 對答案
display((W @ X + b).argmax(axis=1).ravel())
display(y)
In [ ]: