1. 程式人生 > >CS231N assignment1——SVM

CS231N assignment1——SVM

Multiclass Support Vector Machine exercise

Complete and hand in this completed worksheet (including its outputs and any supporting code outside of the worksheet) with your assignment submission. For more details see the assignments page on the course website.

In this exercise you will:

  • implement a fully-vectorized loss function
    for the SVM
  • implement the fully-vectorized expression for its analytic gradient
  • check your implementation using numerical gradient
  • use a validation set to tune the learning rate and regularization strength
  • optimize the loss function with SGD
  • visualize the final learned weights

關鍵點分析

-如何計算這個損失值
-如何計算梯度下降

Loss function計算

Loss function的形式:Li=jyimax(0,sjsyi+), 這裡的常定義為1,如果是的意思是在進行完矩陣乘法的基礎上即樣本集乘上權重矩陣,每個樣本都在每個標籤上有了一個分數,然後目標標籤是yi,累計其他比目標標籤高的標籤的分數,累計求和便是最後的結果了。
如果是直接的計算方式的話,那便是先進行矩陣乘法,然後遍歷每一個樣本,然後取出目標標籤的分數,分別進行運算,但是這樣的運算是需要進行一層迴圈的,類似於K近鄰里面的計算方法,我們希望能夠通過一些運算方式代替這樣的迴圈。
那最直接的想法便是利用廣播機制讓整個矩陣減去目標標籤分數向量再求和。這裡的一個關鍵運算便是取出目標標籤,(說好的不用迴圈就不用。。。),一種方法便是:

target_scores = raw_scores[np.arange(X.shape[0]), y] #np.arrange(X.shape[0])是生成了一個樣本下標的列表,y也是一個列表對應的是一個標籤其實也就是目標標籤的下標

有了這個方式,計算loss function便簡單了,先通過廣播機制計算出最後的分數偏差(預設此處已經加上1),然後通過np,maximum(scores, 0)得到損失值矩陣。這裡面有兩個需要注意的地方,1)因為當時是作為一個整體計算出了分數差,但從公式裡面是可以看到這個損失值是不包括目標標籤自己的,我們需要額外的一步消去這個偏差,2)另外一個結構風險係數不能忘了,用於控制避免最後的過擬合。

  raw_scores = X.dot(W)
  target_scores = raw_scores[np.arange(X.shape[0]), y]  #目標標籤分數
  scores = np.maximum( raw_scores-np.reshape(target_scores, (-1,1))+1, 0)  #得出和目標標籤的分數偏差,取正向值
  scores[np.arange(X.shape[0]), y] = 0  #消去偏差
  margin = np.sum(scores, axis=1)  
  loss += np.sum(margin/X.shape[0]) + np.sum(2*reg*W.dot(W.T))  #分數偏差和結構風險值

梯度迭代值的計算

個人覺得梯度迭代值這裡是很有思考價值的,你可以試著自己好好想想,看下這個需要如何表示。
下面我就幾個方面分析下我做的時候的一些想法:
首先梯度的概念就不做贅述了,如何是一個簡單的W*x,W和x都是一個數,然後要就W進行求導,那麼它的導數就應該只是x,這個大家應該都沒有異議,麻煩的是這個是一個多分類SVM,就想象起來會有些困難,那麼就一步步來吧,先明確下原先計算的式子,看下如果求導的結果應該是什麼樣子的,

Li=jyimax(0,sjsyi+)=jyimax(0,WjxWyix+)
單從一個樣本出發看式子的話,可以分析出如果是對W矩陣求導,就應該是有兩個求導的樣子,對於W矩陣,如果是對於目標標籤列的話,它的求導項應該是樣本值,由於除了目標標籤以外的其他標籤都參與運算了,那最終的結果應該是用標籤列有非0偏差值的列數目乘上這個樣本作為W矩陣對應目標標籤列的梯度值,目標標籤列的長度等於樣本的維度;
如果是對於非目標標籤列的話,它的求導項也應該是樣本值,但這裡有個比較麻煩的地方,就是它是有max運算在其中,運算的結果會決定其實這個式子有沒有對非目標標籤列有求導因子,這裡回憶下上面我們的一個式子,
  scores[np.arange(X.shape[0]), y] = 0

這裡其實就已經有了這個結果了,但是這裡面存的是一個分數,一個參考做法就是使用numpy的一個特殊用法,假定目標Numpy陣列是a,希望A中大於0的元素都變成1:

  import numpy as np
  a = np.array([[-1,0,3],[2,7,1]])
  a[a>0]=0

a>0的操作其實是得到一個大小和a陣列一致的bool矩陣,i行j列的元素代表原矩陣中i行j列的元素是否滿足大於0,最後這個就相當於掩碼,只有掩碼值為真的元素才會得到改變。
所以最後如果是對一個樣本來說,對於W權重矩陣和樣本i,它的梯度應該表示如下:

{Wj=xijyi,scores[i][j]>0Wj=xikyiscores[i][k]1j==yi
下面就分析下如何向量化運算,現在我們先假設這個樣本的維度和權重的維度都是1,然後有N個樣本,c個分類,然後對於每一個樣本我們都有一個矩陣,矩陣是N行C列,代表之前運算分數的結果,如果之前是大於0的就計為1,然後樣本的目標標籤對應的是大於0的分數的列即標籤的數量,記為分數對映權重矩陣
這裡寫圖片描述
通過這幅圖,你可以參考到,如果將左邊的樣本視為1維的矩陣,和右方權重矩陣進行相乘不便是我們想要的結果嗎?單看左邊的一個樣本i和右邊對應的第i行分數對映權重向量,矩陣相乘的結果就是我們想要的樣本i對權重矩陣的梯度結果了。哪怕你最後將N個樣本補充為D維也不會影響目前的形式。之所以最後能以矩陣相乘的形式運算是因為最後的結果也是各個梯度矩陣疊加。所以構造出右矩陣便能計算到我們的目標梯度矩陣了。
最後的最後還有一個trick,記得之前提到的結構風險期望嗎?裡面也是有對W進行計算的,只不過是剛好是它自身和自身元素的點乘,求導的結果便是本身乘上2,這個2如果前面定義loss function時加多一個1/2的係數便可以消掉了,這個就不加贅述了。
  #gradient
  margin[margin>0]=1
  margin[margin<=0]=0

  row_sum = np.sum(margin, axis=1)                  # 1 by N
  margin[np.arange(num_train), y] = -row_sum        # 統計出究竟有幾個標籤對權重模板矩陣有影響
  dW += np.dot(X.T, margin)     # D by C
  dW/=num_train
  dW += reg * W                                     #通過前面的係數消掉了平方求導的係數2