寫在前面
作爲一名準研究生,方向是深度學習,最近在同學的推薦下,開始學習CS231n課程,並在此略做記錄,整理一下框架,方便自己之後複習。
文章內容參考了許多前人的博客與筆記,其實主要就是來自知網-智能單元的翻譯筆記,對內容整理了一下,鏈接在下方,如果有侵權,還請告知;另如有寫錯的地方,望指出。謝謝~
https://zhuanlan.zhihu.com/p/21930884?refer=intelligentunit
kNN分類器
kNN(k-Nearest Neighbor)是比較入門的一個算法,曾經在大三的人工智能課上也學過。
(1)Nearest Neighbor算法:假設現在我們有CIFAR-10的50000張圖片(每種分類5000張)作爲訓練集,我們希望將餘下的10000作爲測試集並給他們打上標籤。Nearest Neighbor算法將會拿着測試圖片和訓練集中每一張圖片去比較,然後將它認爲最相似的那個訓練集圖片的標籤賦給這張測試圖片。
(2)k-Nearest Neighbor算法:它的思想很簡單:與其只找最相近的那1個圖片的標籤,我們找最相似的k個圖片的標籤,然後讓他們針對測試圖片進行投票,最後把票數最高的標籤作爲對測試圖片的預測。所以當k=1的時候,k-Nearest Neighbor分類器就是Nearest Neighbor分類器。從直觀感受上就可以看到,更高的k值可以讓分類的效果更平滑,使得分類器對於異常值更有抵抗力。
評分函數
就放一個最基礎的
損失函數
損失函數是計算評分函數算出來的結果和真實結果相比的不滿意程度
(1)SVM
SVM分類器將它們看做是分類評分,它的損失函數鼓勵正確的分類的分值比其他分類的分值高出至少一個邊界值。
(2)Softmax
Softmax分類器將這些數值看做是每個分類沒有歸一化的對數概率,鼓勵正確分類的歸一化的對數概率變高,其餘的變低。
最優化
最優化是尋找能使得損失函數值最小化的參數W的過程。
(1)隨機搜索
# 假設X_train的每一列都是一個數據樣本(比如3073 x 50000)
# 假設Y_train是數據樣本的類別標籤(比如一個長50000的一維數組)
# 假設函數L對損失函數進行評價
bestloss = float("inf") # Python assigns the highest possible float value
for num in xrange(1000):
W = np.random.randn(10, 3073) * 0.0001 # generate random parameters
loss = L(X_train, Y_train, W) # get the loss over the entire training set
if loss < bestloss: # keep track of the best solution
bestloss = loss
bestW = W
print 'in attempt %d the loss was %f, best %f' % (num, loss, bestloss)
# 輸出:
# in attempt 0 the loss was 9.401632, best 9.401632
# in attempt 1 the loss was 8.959668, best 8.959668
# in attempt 2 the loss was 9.044034, best 8.959668
# in attempt 3 the loss was 9.278948, best 8.959668
# in attempt 4 the loss was 8.857370, best 8.857370
# in attempt 5 the loss was 8.943151, best 8.857370
# in attempt 6 the loss was 8.605604, best 8.605604
# ... (trunctated: continues for 1000 lines)
在上面的代碼中,我們嘗試了若干隨機生成的權重矩陣W,其中某些的損失值較小,而另一些的損失值大些。我們可以把這次隨機搜索中找到的最好的權重W取出,然後去跑測試集,跑的過程略。
驗證集上表現最好的權重W跑測試集的準確率是15.5%,而完全隨機猜的準確率是10%,如此看來,這個準確率對於這樣一個不經過大腦的策略來說,還算不錯嘛!
(2)隨機局部搜索
第一個策略可以看做是每走一步都嘗試幾個隨機方向,如果某個方向是向山下的,就向該方向走一步。這次我們從一個隨機W開始,然後生成一個隨機的擾動\delta W ,只有當W+\delta W的損失值變低,我們纔會更新。這個過程的具體代碼如下:
W = np.random.randn(10, 3073) * 0.001 # 生成隨機初始W
bestloss = float("inf")
for i in xrange(1000):
step_size = 0.0001
Wtry = W + np.random.randn(10, 3073) * step_size
loss = L(Xtr_cols, Ytr, Wtry)
if loss < bestloss:
W = Wtry
bestloss = loss
print 'iter %d loss is %f' % (i, bestloss)
使用同樣的數據(1000),這個方法可以得到21.4%的分類準確率。這個比策略一好,但是依然過於浪費計算資源。
(3)跟隨梯度
梯度是函數的斜率的一般化表達,它不是一個值,而是一個向量。在輸入空間中,梯度是各個維度的斜率組成的向量(或者稱爲導數derivatives)。
對一維函數的求導公式:
下面具體介紹兩種計算梯度的方法:
①數值梯度法
就是使用有限差值近似計算梯度比較簡單,但缺點在於終究只是近似,且耗費計算資源太多。
上節中的公式已經給出數值計算梯度的方法。下面代碼是一個輸入爲函數f和向量x,計算f的梯度的通用函數,它返回函數f在點x處的梯度:
def eval_numerical_gradient(f, x):
"""
一個f在x處的數值梯度法的簡單實現
- f是隻有一個參數的函數
- x是計算梯度的點
"""
fx = f(x) # 在原點計算函數值
grad = np.zeros(x.shape)
h = 0.00001
# 對x中所有的索引進行迭代
it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
while not it.finished:
# 計算x+h處的函數值
ix = it.multi_index
old_value = x[ix]
x[ix] = old_value + h # 增加h
fxh = f(x) # 計算f(x + h)
x[ix] = old_value # 存到前一個值中 (非常重要)
# 計算偏導數
grad[ix] = (fxh - fx) / h # 坡度
it.iternext() # 到下個維度
return grad
根據上面的梯度公式,代碼對所有維度進行迭代,在每個維度上產生一個很小的變化h,通過觀察函數值變化,計算函數在該維度上的偏導數。最後,所有的梯度存儲在變量grad中。
實踐考量:注意在數學公式中,h的取值是趨近於0的,然而在實際中,用一個很小的數值(比如例子中的1e-5)就足夠了。在不產生數值計算出錯的理想前提下,你會使用儘可能小的h。還有,實際中用中心差值公式(centered difference formula)[f(x+h)-f(x-h)]/2h效果較好。
②分析梯度法
利用微分來分析,能得到計算梯度的公式(不是近似),用公式計算梯度速度很快,唯一不好的就是實現的時候容易出錯。
爲了解決這個問題,在實際操作時常常將分析梯度法的結果和數值梯度法的結果作比較,以此來檢查其實現的正確性,這個步驟叫做梯度檢查。
這一部分筆者也還沒理解透,所以直接上截圖
梯度下降
小批量數據梯度下降(Mini-batch gradient descent):在大規模的應用中(比如ILSVRC挑戰賽),訓練數據可以達到百萬級量級。如果像這樣計算整個訓練集,來獲得僅僅一個參數的更新就太浪費了。一個常用的方法是計算訓練集中的小批量(batches)數據。例如,在目前最高水平的卷積神經網絡中,一個典型的小批量包含256個例子,而整個訓練集是多少呢?一百二十萬個。這個小批量數據就用來實現一個參數更新:
# 普通的小批量數據梯度下降
while True:
data_batch = sample_training_data(data, 256) # 256個數據
weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
weights += - step_size * weights_grad # 參數更新
反向傳播
未完,待續