SVM梯度求導及實現

  • 2019 年 10 月 5 日
  • 筆記

SVM梯度求導及實現


0.說在前面1.梯度推導2.實現3.作者的話


0.說在前面

昨晚看了一部電影,叫做我是馬布里,非常正能量,推薦給各位,看完這部電影的總結話是:

冠軍與非冠軍的區別在於你一直並沒有將兩者進行明確界定,只是模糊了兩者的邊緣,我們不是適應邊緣化的人,而是打破邊緣化的創造者!

今天重點來推導SVM梯度及程式碼實現,下面一起來實戰吧!

1.梯度推導

loss fuction

SVM損失函數想要SVM在正確分類上的得分始終比不正確的得分高出一個邊界值!

下面來進行梯度下降的推導!

loss vector

數學推導如下:

X表示如下(1式):

W表示如下(2式):

S表示如下(3式):

X的shape為NxD,W的shape為DxC,S的shape為NxC,

X*W=S  f(S)=L # f表示loss function  

這裡使用鏈式求導法對w進行求導(4式):

由上面知道X的shape為NxD,由於L對S求導的shape為NxC,而NxD矩陣與NxC矩陣不能直接相乘,故要對X進行轉置!

對(4)內部進行求導拆分,如(5)式

s1隻跟L1有關,si只跟Lj有關,於是求和可以去掉,轉化為後面那個Lj對Sj求導!

(5式)

對Lj求和展開(6式)

將Lj式子帶入(5)式,我們知道,與Sj1相關的只有第一項,那麼就是1,當max函數算出的值大於0,對於Sjk(k不等於yj)的時候,求導為1,否則為0;而對於Sjyj的時候,也就是k等於yj的時候求導,當max函數算出的值大於0,求導為-1,否則為0,將所有的值相加就是最後的k=yj求導結果。

2.實現

loss native

def svm_loss_vectorized(W, X, y, reg):      loss = 0.0      dW = np.zeros(W.shape) # initialize the gradient as zero      # (N,D)      # N      num_train = X.shape[0]      # (D,C)      # C      num_classes = W.shape[1]      # (N,C)      scores = X.dot(W)      # 獲取每個樣本對應label的得分      # (N,1)      y_score =scores[np.arange(num_train),y].reshape(-1,1)      mask = (scores-y_score+1)>0      scores = (scores-y_score+1)*mask      loss =(np.sum(scores)-num_train*1)/num_train      loss += reg * np.sum(W * W)      # 初始化ds,      ds = np.ones_like(scores)      # 有效的score梯度為1,無效的為0      ds*=mask      # 去掉k=yj的情況      ds[np.arange(num_train),y]=-1*(np.sum(mask,axis=1)-1)      # 對應公式(4)      dW=np.dot(X.T,ds)/num_train      dW+=2*reg*W      return loss, dW  

這裡的程式碼引自訓練營老師程式碼,這裡寫的很精闢,非常值得學習,特別是對於這個邏輯處理,下面來深入分析一下。

模擬實驗

首先是找到有效分數,也就是上述max()函數大於0與小於0的情況,這裡直接通過先判斷,這裡來模擬一下操作:

首先定義兩個二維數組,分別是x1與x2:x1表示上面公式推導的(3)式子,S矩陣,也就是通過X與W相乘後的矩陣!x2表示預測結果為真實label的yj。

下面一起來學習一下模擬操作以及老師在這裡的運算精髓!

In

x1 = np.array(np.arange(6)).reshape(3,2)  x2 = np.array([2,3,3]).reshape(-1,1)  x1  x2  

Out

array([[0, 1],         [2, 3],         [4, 5]])  array([[2],         [3],         [3]])  

In

mask = x1-x2+1>0  

通過這個操作可以得到什麼?

Out

array([[False, False],         [False,  True],         [ True,  True]])  

發現沒,得到了一個布爾類型的同shape多維數組,那麼是不是可以這樣說,思路就是首先通過得到有效無效分數的布爾高維數組,有效就是True,無效就是False,在運算的時候,直接可以將False看作0,True看作1。

那麼我們接下來計算真實的得分:

In

scores=(x1-x2+1)*mask  scores  

Out

array([[0, 0],         [0, 1],         [2, 3]])  

這個就是我們期望通過下面公式得到的結果!

這裡實現的方法非常巧妙!!!

這樣做的好處是可以避免循環及使用max函數!!

上述的程式碼等價於:

In

for i in range(x1.shape[0]):      for j in range(x1.shape[1]):          x1[i][j]=max(0,x1[i][j]-x2[i][0]+1)  

Out

array([[0, 0],         [0, 1],         [2, 3]])  

注意點

由於上述公式(6)在計算得分時候不涉及yj也就是預測為真正label這一項,而在計算的時候,實際上把所有的都計算了,所以得減去這一項,對於每一行操作都多算了一個:

總共num_train行,所以是num_train*1個!

loss =(np.sum(scores)-num_train*1)/num_train  

另外就是在計算ds的時候,這個ds就是上述(5)式,L對S求導,最終求導結論就是:

(1)當對Sjk求導時(k不等於yj),若為有效得分,則為1,否則為0;

(2)當對Sjk求導時(k等於yj)時,若為有效得分,則為多個-1,否則為0;

第(1)點主要說的是下面這個式子當中的Sjk,第(2)主要說的是下面這個式子中的Sjyj求導,因為是多個max函數累加,那麼對於Sjyj求導的話是每一個max都有一項,所以如果max得分是正數,則表示求導結果是-1,將多個求導的-1疊加就是最後對Sjyj的求導總和;而對於Sjk求導,雖然是多個max,以求Sj1為例,只有一個max中存在Sj1,所以總的求導要麼為1,要麼為0。

所以最後的L對S求導結果用程式碼實現就是:

# 有效的score梯度為1,無效的為0  ds*=mask  # 去掉k=yj的情況  ds[np.arange(num_train),y]=-1*(np.sum(mask,axis=1)-1)  

上面說了mask是將預測為yj的也算進去了,對於當碰到下面這個式子:

由於每一個mask中都多算了一個這個式子,而這個式子總是有效的!!!所以直接減去這次的結果就是去掉k等於yj的情況!

Tags: