https://web.stanford.edu/class/archive/cs/cs224n/cs224n.1174/syllabus.html 第一次作業筆記
Softmax
softmax常數不變性
由于,因此多余的
可以上下消除,于是:
這里
發現了一個Softmax非常好的性質,即使兩個數都很大比如 1000
與 1001
,其結果與 1
和2
的結果相同,即其只關注數字之間的差,而不是差占的比例。
Python實現
之所以介紹Softmax
常數不變性,是因為發現給定的測試用例非常大,直接計算次方
import numpy as np
def softmax(x):
orig_shape = x.shape
if len(x.shape) > 1:
# Matrix
### YOUR CODE HERE
x_max = np.max(x, axis=1).reshape(x.shape[0], 1)
x -= x_max
exp_sum = np.sum(np.exp(x), axis=1).reshape(x.shape[0], 1)
x = np.exp(x) / exp_sum
### END YOUR CODE
else:
# Vector
### YOUR CODE HERE
x_max = np.max(x)
x -= x_max
exp_sum = np.sum(np.exp(x))
x = np.exp(x) / exp_sum
### END YOUR CODE
#or: x = (np.exp(x)/sum(np.exp(x)))
assert x.shape == orig_shape
return x
def test_softmax_basic():
"""
Some simple tests to get you started.
Warning: these are not exhaustive.
"""
print("Running basic tests...")
test1 = softmax(np.array([1,2]))
print(test1)
ans1 = np.array([0.26894142, 0.73105858])
assert np.allclose(test1, ans1, rtol=1e-05, atol=1e-06)
test2 = softmax(np.array([[1001,1002],[3,4]]))
print(test2)
ans2 = np.array([
[0.26894142, 0.73105858],
[0.26894142, 0.73105858]])
assert np.allclose(test2, ans2, rtol=1e-05, atol=1e-06)
test3 = softmax(np.array([[-1001,-1002]]))
print(test3)
ans3 = np.array([0.73105858, 0.26894142])
assert np.allclose(test3, ans3, rtol=1e-05, atol=1e-06)
print("You should be able to verify these results by hand!\n")
if __name__ == "__main__":
test_softmax_basic()
神經網絡基礎
梯度檢查
Sigmoid導數
定義如下,發現
。
即:
交叉熵定義
當使用交叉熵作為評價指標時,求梯度:
- 已知:
- 交叉熵:
其中是指示變量,如果該類別和樣本的類別相同就是1,否則就是0。因為y一般為one-hot類型。
而 表示每種類型的概率,概率已經過softmax計算。
對于交叉熵其實有多重定義的方式,但含義相同:
分別為:
二分類定義
- y——表示樣本的label,正類為1,負類為0
- p——表示樣本預測為正的概率
多分類定義
- y——指示變量(0或1),如果該類別和樣本的類別相同就是1,否則是0;
- p——對于觀測樣本屬于類別c的預測概率。
但表示的意思都相同,交叉熵用于反映 分類正確時的概率情況。
Softmax導數
進入解答:
- 首先定義
和分子分母。
-
對
求導:
注意: 分子是
,分母是所有的
,而求偏微的是
。
- 因此,根據i與j的關系,分為兩種情況:
- 當
時:
$f_i' = e^{\theta_i}$,$g_i' = e^{\theta_j}$
$\begin{align} \frac{\partial{S_i}}{\partial{\theta_j}} &=\frac{e^{\theta_i}\sum^{k}_{k=1}e^{\theta_k} - e^{\theta_i}e^{\theta_j}}{(\sum^{k}_{k=1}e^{\theta_k})^2} \\ &= \frac{e^{\theta_{i}}}{\sum_{k} e^{\theta_{k}}} \times \frac{\sum_{k} e^{\theta_{k}} – e^{\theta_{j}}}{\sum_{k} e^{\theta_{k}}} \nonumber \\ &= S_{i} \times (1 – S_{i}) \end{align}$
- 當
時:
$f'_{i} = 0 $,$g'_{i} = e^{\theta_{j}}$
$\begin{align} \frac{\partial{S_i}}{\partial{\theta_j}} &= \frac{0 – e^{\theta_{j}} e^{\theta_{i}}}{(\sum_{k} e^{\theta_{k}})^{2}} \\&= – \frac{e^{\theta_{j}}}{\sum_{k} ^{\theta_{k}}} \times \frac{e^{\theta_{i}}}{\sum_{k} e^{\theta_{k}}} \\ &=-S_j \times S_i\end{align}$
交叉熵梯度
計算 ,根據鏈式法則,
$\begin{align} \frac{\partial CE}{\partial \theta_{i}} &= – \sum_{k} y_{k} \frac{\partial log S_{k}}{\partial \theta_{i}} \\&= – \sum_{k} y_{k} \frac{1}{S_{k}} \frac{\partial S_{k}}{\partial \theta_{i}} \\ &= – y_{i} (1 – S_{i}) – \sum_{k \ne i} y_{k} \frac{1}{S_{k}} (-S_{k} \times S_{i}) \\ &= – y_{i} (1 – S_{i}) + \sum_{k \ne i} y_{k} S_{i} \\ &= S_{i}(\sum_{k} y_{k}) – y_{i}\end{align}$
因為,所以
反向傳播計算神經網絡梯度
根據題目給定的定義:
已知損失函數,
,
求,
,
,
,
解答:
反向傳播,定義,
:
對于輸出層來說,
的輸入為
,而輸出則為
上小節計算得到 的梯度為
,
可以使用 替代
,得到
# 推測這里使用點乘的原因是
經過計算后,應該是一個標量,而不是向量。
于是得到:
與計算相似,計算
如果仍然對反向傳播有疑惑
可以參考一文弄懂神經網絡中的反向傳播法——BackPropagation,畫圖出來推導一下。
如何直觀地解釋 backpropagation 算法? - Anonymous的回答 - 知乎
https://www.zhihu.com/question/27239198/answer/89853077
參數數量
代碼實現
- sigmoid和對應的梯度
def sigmoid(x):
s = 1 / (1 + np.exp(-x))
return s
def sigmoid_grad(s):
ds = s * (1-s)
return ds
- 梯度檢查
import numpy as np
import random
# First implement a gradient checker by filling in the following functions
def gradcheck_naive(f, x):
""" Gradient check for a function f.
Arguments:
f -- a function that takes a single argument and outputs the
cost and its gradients
x -- the point (numpy array) to check the gradient at
"""
rndstate = random.getstate()
random.setstate(rndstate)
fx, grad = f(x) # Evaluate function value at original point
h = 1e-4 # Do not change this!
# Iterate over all indexes in x
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
ix = it.multi_index
print(ix)
# Try modifying x[ix] with h defined above to compute
# numerical gradients. Make sure you call random.setstate(rndstate)
# before calling f(x) each time. This will make it possible
# to test cost functions with built in randomness later.
### YOUR CODE HERE:
x[ix] += h
new_f1 = f(x)[0]
x[ix] -= 2*h
random.setstate(rndstate)
new_f2 = f(x)[0]
x[ix] += h
numgrad = (new_f1 - new_f2) / (2 * h)
### END YOUR CODE
# Compare gradients
reldiff = abs(numgrad - grad[ix]) / max(1, abs(numgrad), abs(grad[ix]))
if reldiff > 1e-5:
print("Gradient check failed.")
print("First gradient error found at index %s" % str(ix))
print("Your gradient: %f \t Numerical gradient: %f" % (
grad[ix], numgrad))
return
it.iternext() # Step to next dimension
print("Gradient check passed!")
- 反向傳播
def forward_backward_prop(data, labels, params, dimensions):
"""
Forward and backward propagation for a two-layer sigmoidal network
Compute the forward propagation and for the cross entropy cost,
and backward propagation for the gradients for all parameters.
Arguments:
data -- M x Dx matrix, where each row is a training example.
labels -- M x Dy matrix, where each row is a one-hot vector.
params -- Model parameters, these are unpacked for you.
dimensions -- A tuple of input dimension, number of hidden units
and output dimension
"""
### Unpack network parameters (do not modify)
ofs = 0
Dx, H, Dy = (dimensions[0], dimensions[1], dimensions[2])
W1 = np.reshape(params[ofs:ofs+ Dx * H], (Dx, H))
ofs += Dx * H
b1 = np.reshape(params[ofs:ofs + H], (1, H))
ofs += H
W2 = np.reshape(params[ofs:ofs + H * Dy], (H, Dy))
ofs += H * Dy
b2 = np.reshape(params[ofs:ofs + Dy], (1, Dy))
### YOUR CODE HERE: forward propagation
h = sigmoid(np.dot(data,W1) + b1)
yhat = softmax(np.dot(h,W2) + b2)
### END YOUR CODE
### YOUR CODE HERE: backward propagation
cost = np.sum(-np.log(yhat[labels==1]))
d1 = (yhat - labels)
gradW2 = np.dot(h.T, d1)
gradb2 = np.sum(d1,0,keepdims=True)
d2 = np.dot(d1,W2.T)
# h = sigmoid(z_1)
d3 = sigmoid_grad(h) * d2
gradW1 = np.dot(data.T,d3)
gradb1 = np.sum(d3,0)
### END YOUR CODE
### Stack gradients (do not modify)
grad = np.concatenate((gradW1.flatten(), gradb1.flatten(),
gradW2.flatten(), gradb2.flatten()))
return cost, grad
word2vec
關于詞向量的梯度
在以softmax為假設函數的word2vec中
是中央單詞的詞向量
(
) 是第
個詞語的詞向量。
假設使用交叉熵作為損失函數, 為正確單詞 (one-hot向量的第
維為1),請推導損失函數關于
的梯度。
提示:
其中 =
,
,
,
是所有詞向量構成的矩陣。
解答:
首先明確本題給定的模型是skip-gram
,通過給定中心詞,來發現周圍詞的。
定義 ,
表示所有詞向量組成的矩陣,而
也表示的是一個詞向量。
hint: 如果兩個向量相似性越高,則乘積也就越大。想象一下余弦夾角,應該比較好明白。
因為中所有的詞向量,都和
乘一下獲得
。
是干嘛用的呢?
內就有W個值,每個值表示和
相似程度,通過這個相似度
選出最大值,然后與實際對比,進行交叉熵的計算。
已知: 和
因此:
除了上述表示之外,還有另一種計算方法
[圖片上傳失敗...(image-53cc75-1557025564256)]
于是:
仔細觀察這兩種寫法,會發現其實是一回事,都是 觀察與期望的差()。
推導lookup-table梯度
與詞向量相似
負采樣時的梯度推導
假設進行負采樣,樣本數為,正確答案為
,那么有
。負采樣損失函數定義如下:
其中:
解答:
首先說明一下,從哪里來的,參考note1 第11頁,會有一個非常詳細的解釋。
全部梯度
推導窗口半徑的上下文[word
,...,word
,word
,word
,...,word
]時,skip-gram 和 CBOW的損失函數
(
是正確答案的詞向量)或說
或
關于每個詞向量的梯度。
對于skip-gram來講,的上下文對應的損失函數是:
這里? 是離中心詞距離
的那個單詞。
而CBOW稍有不同,不使用中心詞而使用上下文詞向量的和
作為輸入去預測中心詞:
然后CBOW的損失函數是:
解答:
根據前面的推導,知道如何得到梯度和
。那么所求的梯度可以寫作:
skip-gram
CBOW
補充部分:
-
矩陣的每個行向量的長度歸一化
x = x/np.linalg.norm(x,axis=1,keepdims=True)
-
在斯坦福情感樹庫上訓練詞向量
直接運行
q3_run
即可image
情感分析
特征向量
最簡單的特征選擇方法就是取所有詞向量的平均
sentence_index = [tokens[i] for i in sentence]
for index in sentence_index:
sentVector += wordVectors[index, :]
sentVector /= len(sentence)
正則化
values = np.logspace(-4, 2, num=100, base=10)
調參
bestResult = max(results, key= lambda x: x['dev'])
懲罰因子對效果的影響
confusion matrix
關聯性排序的一個東西,對角線上的元素越多,預測越準確。