【DL-CV】線性分類器<前篇---後篇>【DL-CV】反向傳播,(隨機)梯度降低python
如今有一個模型,能對輸入的圖像各類可能的類別進行評分。咱們會引入損失函數Loss Function(或叫代價函數 Cost Function)定量的衡量該模型(也就是權重W)的好壞,其原理是——輸出結果與真實結果之間差別越大,損失函數輸出越大,模型越糟糕(須要訓練讓損失變小)。segmentfault
根據差別定義的不一樣,損失函數有不一樣的計算公式,這裏介紹在圖像識別中最經常使用的兩個損失——多類別SVM損失(或折葉損失hinge loss)和交叉熵損失,分別對應多類別SVM分類器和Softmax分類器
並且爲了方便介紹,咱們繼續以圖片評分的例子爲例數組
第i個數據中包含圖像xi 的像素和表明正確類別的標籤yi(一個表明類別的數字),xi通過模型後輸出sj (j對應某個類別的數字,sj對應該類別的分數),瞭解這些後咱們先拋出每一個數據損失計算公式:網絡
模型越好,正確類別的得分應該要比其餘錯誤類別的得分高,至於高多少,這個閾值(Δ)由咱們來定,若是高出閾值,咱們認爲正確類別和某個類別的區分很好,咱們給一個0損失給這兩個類別的區分。相反若是某個錯誤類別比正確類別的得分高,說明該模型對這兩類別的區分很糟,咱們把高多少這個值加上閾值做爲其損失。求正確類別和其餘錯誤類別兩兩的損失值的和做爲該數據的總損失。
一個具體例子以下圖函數
這種損失也叫折葉損失,因其使用max(0,-)而得名,是標準經常使用的用法。有時候也會使用平方折葉損失SVM(L2-SVM),它使用的是max(x,-)2,這會放大損失(對壞的方面更加敏感),有些數據集使用L2-SVM會有更好的效果,能夠經過交叉驗證來決定到底使用哪一個。spa
這裏插入講下正則化的問題code
上面損失函數有一個問題。假設有一個數據集和一個權重集W可以正確地分類每一個數據(對於全部的i都有Li=0)時,這個W並不惟一,好比當λ>1時,任何數乘λW都能使得損失值爲0,由於這個變化將全部分值的大小都均等地擴大了;又好比可能存在W的某一部分很大,另一部分幾乎爲0。咱們並不想要一堆擴大了N倍的W或者極不均勻的W,這時候就要向損失函數增長一個正則化懲罰(regularization penalty)R(W)來抑制大數值權重了。
須要注意的是正則化損失R(W)並非加在每一個數據的損失上,而是加在全部一組訓練集(N個數據)損失的平均值上,這樣咱們獲得最終的損失函數L,λ是正則化強度。blog
最經常使用的正則化懲罰是L2範式,L2範式經過對全部參數進行逐元素的平方懲罰來抑制大數值的權重:圖片
正則化很重要,正則化的做用也不止於此,更多做用後面會在介紹,在此你只要知道正則化的做用是提高分類器的泛化能力,每一個損失函數都應該引入 λR(W)get
Δ是一個超參數,該超參數在絕大多數狀況下設爲Δ=1.0就好了。
權重W的大小對於類別分值有直接影響(固然對他們的差別也有直接影響):當咱們將W中值縮小,類別分值之間的差別也變小,反之亦然。所以,不一樣類別分值之間的邊界的具體值(好比Δ=1或Δ=100)從某些角度來看是沒意義的,由於權重本身就能夠控制差別變大和縮小。也就是說,真正的權衡是咱們容許權重可以變大到何種程度(經過正則化強度λ來控制)。
先來了解Softmax函數,這是一種壓縮函數,實現歸一化的
$$P(s)={e^{s_k}\over \sum_je^{s_j}}$$
Softmax函數接收一組評分輸出s(有正有負),對每一個評分sk進行指數化確保正數,分母是全部評分指數化的和,分子是某個評分指數化後的值,這樣就起到了歸一化的做用。某個類的分值越大,指數化後越大,函數輸出越接近於1,能夠把輸出看作該類別的機率值,全部類別的機率值和爲一。若是正確類別的機率值越接近0,則該模型越糟,應用這個特性,咱們經過對正確類別的機率值取-log來做爲損失(正確類別的機率越小,損失越大),因而咱們獲得
$$L_i=-log({e^{s_{y_i}}\over \sum_je^{s_j}})$$
這就是交叉熵損失計算公式(有時也叫非正式名Softmax損失),具體例子以下圖,使用上不要忘記加正則化λR(W)哦
由於交叉熵損失涉及指數函數,若是趕上很大或很小的分值,計算時會溢出。s很大es會上溢出;s是負數且|s|很大,es會四捨五入爲0致使下溢出,分母爲0就很差了。爲解決這個潛在的問題,咱們要在計算前對得分數據處理一下。取全部得分的最大值M = max(sk), k=1,2,3...,令全部得分都減去這個M。這不會影響損失Softmax函數的輸出,天然也不會影響損失,但這一下解決了溢出問題。要證實也很簡單:es-M = es / eM , 而分子分母會約掉 eM
但仍然存在一個問題,若是分子發生下溢出致使Softmax函數輸出0,取對數時就會獲得−∞,這是錯誤的。爲解決這個問題,其實咱們把上面的變換代進去繼續算就會發現本身解決了
求和項裏必定會有一個e0=1,最終對大於1和取對數不會發生溢出了,最後損失公式變成這樣:
$$L_i=-\log({e^{s_{y_i}}\over \sum_je^{s_j}})=-\log({e^{({s_{y_i}}-M)}\over \sum_je^{({s_j}-M)}})=\log(\sum_j{e^{(s_j-M)}})-(s_{y_i}-M)$$
咱們用一組數據來探究它們的區別(假設SVM損失中的Δ=1),有三組輸出分數[10,-2,3], [10,9,9],[10,-100,-100],正確類別的得分都是10,易得三組數據的SVM損失都是0,但它們的交叉熵損失明顯是有高低之分的。對於SVM損失,它關心的是邊界區分,正確類別的得分其餘得分高出Δ就完事了,損失爲0了。但對於交叉熵損失,因爲正確類別的機率與分數間的差別是有關的,損失不可能等於0,正確類別的得分無窮大,其餘得分無窮小,損失才趨於0。換句話說,交叉熵損失永遠有縮小的空間,它但願評分模型完美;而SVM損失只須要評分模型好到必定程度就好了。
但實際使用上,它們常常是類似的,一般說來,兩種損失函數的表現差異很小,大可沒必要糾結使用哪一個
注:如下代碼基於單層網絡而且不考慮激活函數(圖像數據與權重相乘獲得分數)進行損失統計,目的是爲了集中介紹損失函數的numpy實現。x是二維數組,是N個樣本的數據,每行是該樣本的像素數據(已展開),所以這裏採用x*W。y是一維數組,包含每一個樣本的真實類別(一個數字)。
import numpy as np # SVM損失函數實現 def svm_loss_naive(W, x, y, reg): """ 循環實現 """ train_num = x.shape[0] # 樣本數量 classes_num = W.shape[1] # 類別數量 loss = 0.0 for i in range(train_num): # 計算某個樣本的損失值 scores = x[i].dot(W) correct_class_score = scores[y[i]] # 提取該樣本的真實類別分數 for j in range(classes_num): # 正確類別得分與其餘得分比較 if j == y[i]: continue margin = scores[j] - correct_class_score + 1 # 這裏設閾值爲1 if margin > 0: # 形成損失,將其計入 loss += margin loss /= train_num loss += reg * np.sum(W * W) # 加上正則化損失 return loss def svm_loss_vectorized(W, x, y, reg): """ 最高效向量化運算,維持x的二維結構運算 """ train_num = x.shape[0] classes_num = W.shape[1] scores = x.dot(W) # 二維結構,每行是該樣本各個類別的得分 correct_class_scores = scores[np.arange(train_num), y] # 提取每一個樣本的真實類別分數 correct_class_scores = np.repeat(correct_class_scores,classes_num).reshape(train_num,classes_num) # 擴展至二維結構(與scores同形狀),每一行都是該樣本真實類別的得分 margins = scores - correct_class_scores + 1.0 margins[range(train_num), y] = 0 # 令正確類別與自身相比的loss爲0 抵消 +1.0 loss = (np.sum(margins[margins > 0])) / train_num # 把正數(loss)全加起來除以樣本數得最終損失 loss += reg * np.sum(W*W) # 加上正則化損失 return loss
import numpy as np # Softmax 損失函數實現 def softmax_loss_naive(W, x, y, reg): """ 循環實現 """ loss = 0.0 classes_num = W.shape[1] # 類別數量 train_num = x.shape[0] # 樣本數量 for i in range(train_num): # 計算某個樣本的損失值 score = x[i].dot(W) score -= np.max(score) # 減去最大值防指數運算溢出 correct_class_score = score[y[i]] # 提取該樣本的真實類別分數 exp_sum = np.sum(np.exp(score)) loss += np.log(exp_sum) - correct_class_score # 每一個樣本的損失疊加 loss /= train_num loss += 0.5 * reg * np.sum(W*W) # 加上正則化損失 return loss def softmax_loss_vectorized(W, x, y, reg): """ 最高效向量化運算,維持x的二維結構運算 """ classes_num = W.shape[1] train_num = x.shape[1] scores = x.dot(W) # 二維結構,每行是該樣本各個類別的得分 scores -= np.repeat(np.max(scores, axis=1), classes_num).reshape(scores.shape) # 減去最大值防指數運算溢出 exp_scores = np.exp(scores) # 指數化 correct_class_scores = scores[range(train_num), y] # 提取每一個樣本的真實類別分數 sum_exp_scores = np.sum(exp_scores, axis=1) # 每一個樣本的指數和 loss = np.sum(np.log(sum_exp_scores) - correct_class_scores) # 全部樣本總損失 loss /= train_num loss += reg * np.sum(W*W) expand_sum_exp_scores = np.repeat(sum_exp_scores, classes_num).reshape(scores.shape) # 對每一個樣本的指數和進行擴展,與scores進行除法運算 return loss