基於DL的計算機視覺(3)-- SVM和Softmax分類器

1. 線性分類器

深度學習與計算機視覺系列(2)我們提到了圖像識別的問題,同時提出了一種簡單的解決方法——KNN。然後我們也看到了KNN在解決這個問題的時候,雖然實現起來非常簡單,但是有很大的弊端:

  • 分類器必須記住全部的訓練數據(因爲要遍歷找近鄰啊!!),而在任何實際的圖像訓練集上,數據量很可能非常大,那麼一次性載入內存,不管是速度還是對硬件的要求,都是一個極大的挑戰。
  • 分類的時候要遍歷所有的訓練圖片,這是一個相當相當相當耗時的過程。

這個部分我們介紹一類新的分類器方法,而對其的改進和啓發也能幫助我們自然而然地過渡到深度學習中的卷積神經網。有兩個重要的概念:

  • 得分函數/score function:將原始數據映射到每個類的打分的函數
  • 損失函數/loss function:用於量化模型預測結果和實際結果之間吻合度的函數

在我們得到損失函數之後,我們就將問題轉化成爲一個最優化的問題,目標是得到讓我們的損失函數取值最小的一組參數。

2. 得分函數/score function

首先我們定義一個有原始的圖片像素值映射到最後類目得分的函數,也就是這裏提到的得分函數。先籠統解釋一下,一會兒我們給個具體的實例來說明。假設我們的訓練數據爲xiRD,對應的標籤yi,這裏i=1N表示N個樣本,yi1K表示K類圖片。

比如CIFAR-10數據集中N=50000,而D=32x32x3=3072像素,K=10,因爲這時候我們有10個不同的類別(狗/貓/車…),我們實際上要定義一個將原始像素映射到得分上函數 f:RDRK

2.1 線性分類器

我們先丟出一個簡單的線性映射: 


f(xi,W,b)=Wxi+b 

在這個公式裏,我們假定圖片的像素都平展爲[D x 1]的向量。然後我們有兩個參數:W是[K x D]的矩陣,而向量b爲[K x 1]的。在CIFAR-10中,每張圖片平展開得到一個[3072 x 1]的向量,那W就應該是一個[10 x 3072]的矩陣,b爲[10 x 1]的向量。

這樣,以我們的線性代數知識,我們知道這個函數,接受3072個數作爲輸入,同時輸出10個數作爲類目得分。我們把W叫做權重,b叫做偏移向量

說明幾個點:

  • 我們知道一次矩陣運算,我們就可以藉助W把原始數據映射爲10個類別的得分。
  • 其實我們的輸入(xi,yi)其實是固定的,我們現在要做的事情是,我們要調整W, b使得我們的得分結果和實際的類目結果最爲吻合。
  • 我們可以想象到,這樣一種分類解決方案的優勢是,一旦我們找到合適的參數,那麼我們最後的模型可以簡化到只有保留W, b即可,而所有原始的訓練數據我們都可以不管了。
  • 識別階段,我們需要做的事情僅僅是一次矩陣乘法和一次加法,這個計算量相對之前…不要小太多好麼…

提前劇透一下,其實卷積神經網做的事情也是類似的,將原始輸入的像素映射成類目得分,只不過它的中間映射更加複雜,參數更多而已…

2.2 理解線性分類器

我們想想,其實線性分類器在做的事情,是對每個像素點的三個顏色通道,做計算。咱們擬人化一下,幫助我們理解,可以認爲設定的參數/權重不同會影響分類器的『性格』,從而使得分類器對特定位置的顏色會有自己的喜好

舉個例子,假如說我們的分類器要識別『船隻』,那麼它可能會喜歡圖片的四周都是藍色(通常船隻是在水裏海里吧…)。

我們用一個實際的例子來表示這個得分映射的過程,大概就是下圖這個樣子:

得分函數

原始像素點向量xi經過W和b映射爲對應結果類別的得分f(xi,W,b)=Wxi+b。不過上面這組參數其實給的是不太恰當的,因爲我們看到在這組參數下,圖片屬於狗狗的得分最高 -_-||

2.2.1 劃分的第1種理解

圖片被平展開之後,向量維度很高,高維空間比較難想象。我們簡化一下,假如把圖片像素輸入,看做可以壓縮到二維空間之中的點,那我們想想,分類器實際上在做的事情就如下圖所示:

分割

W中的每一列對應類別中的每一類,而當我們改變W中的值的時候,圖上的線的方向會跟着改變,那麼b呢?對,b是一個偏移量,它表示當我們的直線方向確定以後,我們可以適當平移直線到合適的位置。沒有b會怎麼樣呢,如果直線沒有偏移量,那意味着所有的直線都要通過原點,這種強限制條件下顯然不能保證很好的平面類別分割。

2.2.2 劃分的第2種理解

對W第二種理解方式是,W的每一行可以看做是其中一個類別的模板。而我們輸入圖片相對這個類別的得分,實際上是像素點和模板匹配度(通過內積運算獲得),而類目識別實際上就是在匹配圖像和所有類別的模板,找到匹配度最高的那個。

是不是感覺和KNN有點類似的意思?是有那麼點相近,但是這裏我們不再比對所有圖片,而是比對類別的模板,這樣比對次數只和類目數K有關係,所以自然計算量要小很多,同時比對的時候用的不再是l1或者l2距離,而是內積計算。

我們提前透露一下CIFAR-10上學習到的模板的樣子:

CIFAR-10模板

你看,和我們設想的很接近,ship類別的周邊有大量的藍色,而car的旁邊是土地的顏色。

2.2.3 關於偏移量的處理

我們先回到如下的公式: 


f(xi,W,b)=Wxi+b 

公式中有W和b兩個參數,我們知道調節兩個參數總歸比調節一個參數要麻煩,所以我們用一點小技巧,來把他們組合在一起,放到一個參數中。

我們現在要做的運算是矩陣乘法再加偏移量,最常用的合併方法就是,想辦法把b合併成W的一部分。我們仔細看看下面這張圖片:

合併W與b

我們給輸入的像素矩陣加上一個1,從而把b拼接到W裏變成一個變量。依舊拿CIFAR-10舉例,原本是[3072 x 1]的像素向量,我們添上最後那個1變成[3073 x 1]的向量,而[W]變成[W b]。

2.2.4 關於數據的預處理

插播一段,實際應用中,我們很多時候並不是把原始的像素矩陣作爲輸入,而是會預先做一些處理,比如說,有一個很重要的處理叫做『去均值』,他做的事情是對於訓練集,我們求得所有圖片像素矩陣的均值,作爲中心,然後輸入的圖片先減掉這個均值,再做後續的操作。有時候我們甚至要對圖片的幅度歸一化/scaling。去均值是一個非常重要的步驟,原因我們在後續的梯度下降裏會提到。

2.3 損失函數

我們已經通過參數W,完成了由像素映射到類目得分的過程。同時,我們知道我們的訓練數據(xi,yi)是給定的,我們可以調整的是參數/權重W,使得這個映射的結果和實際類別是吻合的。

我們回到最上面的圖片中預測 [貓/狗/船] 得分的例子裏,這個圖片中給定的W顯然不是一個合理的值,預測的結果和實際情況有很大的偏差。於是我們現在要想辦法,去把這個偏差表示出來,擬人一點說,就是我們希望我們的模型在訓練的過程中,能夠對輸出的結果計算並知道自己做的好壞。

而能幫助我們完成這件事情的工具叫做『損失函數/loss function』,其實它還有很多其他的名字,比如說,你說不定在其他的地方聽人把它叫做『代價函數/cost function』或者『客觀度/objective』,直觀一點說,就是我們輸出的結果和實際情況偏差很大的時候,損失/代價就會很大。

2.3.1 多類別支持向量機損失/Multiclass Support Vector Machine loss

膩害的大神們定義出了好些損失函數,我們這裏首先要介紹一種極其常用的,叫做多類別支持向量機損失(Multiclass SVM loss)。如果要用一句精簡的話來描述它,就是它(SVM)希望正確的類別結果獲得的得分比不正確的類別,至少要高上一個固定的大小Δ

我們先解釋一下這句話,一會兒再舉個例子說明一下。對於訓練集中的第i張圖片數據xi,我們的得分函數,在參數W下會計算出一個所有類得分結果f(xi,W),其中第j類得分我們記作f(xi,W)j,該圖片的實際類別爲yi,則對於第i張樣本圖片,我們的損失函數是如下定義的:


Li=jyimax(0,f(xi,W)jf(xi,W)yi+Δ) 

看公式容易看瞎,譯者也經常深深地爲自己智商感到捉急,我們舉個例子來解釋一下這個公式。 
假如我們現在有三個類別,而得分函數計算某張圖片的得分爲f(xi,W)=[13,7,11],而實際的結果是第一類(yi=0)。假設Δ=10(這個參數一會兒會介紹)。上面的公式把錯誤類別(jyi)都遍歷了一遍,求值加和: 

Li=max(0,713+10)+max(0,1113+10)

仔細看看上述的兩項,左邊項-10和0中的最大值爲0,因此取值是零。其實這裏的含義是,實際的類別得分13要比第二類得分-7高出20,超過了我們設定的正確類目和錯誤類目之間的最小margin Δ=10,因此第二類的結果我們認爲是滿意的,並不帶來loss,所以值爲0。而第三類得分11,僅比13小2,沒有大於Δ=10,因此我們認爲他是有損失/loss的,而損失就是當前距離2距離設定的最小距離Δ的差距8。

注意到我們的得分函數是輸入像素值的一個線性函數(f(xi;W)=Wxi),因此公式又可以簡化爲(其中wj是W的第j行): 


Li=jyimax(0,wTjxiwTyixi+Δ) 

我們還需要提一下的是,關於損失函數中max(0,-)的這種形式,我們也把它叫做hinge loss/鉸鏈型損失,有時候你會看到squared hinge loss SVM(也叫L2-SVM),它用到的是max(0,)2,這個損失函數懲罰那些在設定Δ距離之內的錯誤類別的懲罰度更高。兩種損失函數標準在特定的場景下效果各有優劣,要判定用哪個,還是得藉助於交叉驗證/cross-validation。

對於損失函數的理解,可以參照下圖: 


損失函數的理解 

2.3.2 正則化

如果大家仔細想想,會發現,使用上述的loss function,會有一個bug。如果參數W能夠正確地識別訓練集中所有的圖片(損失函數爲0)。那麼我們對M做一些變換,可以得到無數組也能滿足loss function=0的參數W’(舉個例子,對於λ>1的所有λW,原來的錯誤類別和正確類別之間的距離已經大於Δ,現在乘以λ,更大了,顯然也能滿足loss爲0)。

於是…我們得想辦法把W參數的這種不確定性去除掉啊…這就是我們要提到的正則化,我們需要在原來的損失函數上再加上一項正則化項(regularization penalty R(W)),最常見的正則化項是L2範數,它會對幅度很大的特徵權重給很高的懲罰: 


R(W)=klW2k,l 

根據公式可以看到,這個表達式R(W)把所有W的元素的平方項求和了。而且它和數據本身無關,只和特徵權重有關係。

我們把兩部分組(數據損失/data loss和正則化損失/regularization loss)在一起,得到完整的多類別SVM損失權重,如下: 


L=1NiLidata loss+λR(W)regularization loss 

也可以展開,得到更具體的完整形式: 


L=1Nijyi[max(0,f(xi;W)jf(xi;W)yi+Δ)]+λklW2k,l 

其中N是訓練樣本數,我們給正則化項一個參數λ,但是這個參數的設定只有通過實驗確定,對…還是得交叉驗證/cross-validation。

關於設定這樣一個正則化懲罰項爲什麼能解決W的不確定性,我們在之後的系列裏會提到,這裏我們舉個例子簡單看看,這個項是怎麼起到作用的。

假定我們的輸入圖片像素矩陣是x=[1,1,1,1],而現在我們有兩組不同的W權重參數中對應的向量w1=[1,0,0,0]w2=[0.25,0.25,0.25,0.25]。那我們很容易知道wT1x=wT2x=1,所以不加正則項的時候,這倆得到的結果是完全一樣的,也就意味着——它們是等價的。但是加了正則項之後,我們發現w2總體的損失函數結果更小(因爲4*0.25^2<1),於是我們的系統會選擇w2,這也就意味着系統更『喜歡』權重分佈均勻的參數,而不是某些特徵權重明顯高於其他權重(佔據絕對主導作用)的參數。

之後的系列裏會提到,這樣一個平滑的操作,實際上也會提高系統的泛化能力,讓其具備更高的通用性,而不至於在訓練集上過擬合

另外,我們在討論過擬合的這個部分的時候,並沒有提到b這個參數,這是因爲它並不具備像W一樣的控制輸入特徵的某個維度影響力的能力。還需要說一下的是,因爲正則項的引入,訓練集上的準確度是會有一定程度的下降的,我們永遠也不可能讓損失達到零了(因爲這意味着正則化項爲0,也就是W=0)。

下面是簡單的計算損失函數(沒加上正則化項)的代碼,有未向量化向量化兩種形式:

<code class="language-python hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">L_i</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(x, y, W)</span>:</span>
  <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"""
  未向量化版本. 
  對給定的單個樣本(x,y)計算multiclass svm loss.
  - x: 代表圖片像素輸入的向量 (例如CIFAR-10中是3073 x 1,因爲添加了bias項對應的1到x中)
  - y: 圖片對應的類別編號(比如CIFAR-10中是0-9)
  - W: 權重矩陣 (例如CIFAR-10中是10 x 3073)
  """</span>
  delta = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.0</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 設定delta</span>
  scores = W.dot(x) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 內積計算得分</span>
  correct_class_score = scores[y]
  D = W.shape[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 類別數:例如10</span>
  loss_i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.0</span>
  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> j <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> xrange(D): <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 遍歷所有錯誤的類別</span>
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> j == y:
      <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 跳過正確類別</span>
      <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">continue</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 對第i個樣本累加loss</span>
    loss_i += max(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, scores[j] - correct_class_score + delta)
  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> loss_i

<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">L_i_vectorized</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(x, y, W)</span>:</span>
  <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">""" 
  半向量化的版本,速度更快。
  之所以說是半向量化,是因爲這個函數外層要用for循環遍歷整個訓練集 -_-||
  """</span> 
  delta = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.0</span>
  scores = W.dot(x)
  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 矩陣一次性計算</span>
  margins = np.maximum(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, scores - scores[y] + delta)

  margins[y] = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> 
  loss_i = np.sum(margins)
  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> loss_i

<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">L</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(X, y, W)</span>:</span>
  <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">""" 
  全向量化實現 :
  - X: 包含所有訓練樣本中數據(例如CIFAR-10是3073 x 50000)
  - y: 所有的類別結果 (例如50000 x 1的向量)
  - W: 權重矩陣 (例如10 x 3073)
  """</span>
  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#待完成...</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li></ul>

說到這裏,其實我們的損失函數,是提供給我們一個數值型的表示,來衡量我們的預測結果和實際結果的差別。而要提高預測的準確性,要做的事情是,想辦法最小化這個loss。

2.4 一些現實的考慮點

2.4.1 設定Delta

我們在計算Multi SVM loss的時候,Δ是我們提前設定的一個參數。這個值咋設定?莫不是…也需要交叉驗證…?其實基本上大部分的場合下我們設定Δ=1.0都是一個安全的設定。我們看公式中的參數Δλ似乎是兩個截然不同的參數,實際上他倆做的事情比較類似,都是儘量讓模型貼近標準預測結果的時候,在 數據損失/data loss和 正則化損失/regularization loss之間做一個交換和平衡

在損失函數計算公式裏,可以看出,權重W的幅度對類別得分有最直接的影響,我們減小W,最後的得分就會減少;我們增大W,最後的得分就增大。從這個角度看,Δ這個參數的設定(Δ=1或者Δ=100),其實無法限定W的伸縮。而真正可以做到這點的是正則化項,λ的大小,實際上控制着權重可以增長和膨脹的空間

2.4.2 關於二元/Binary支持向量機

如果大家之前接觸過Binary SVM,我們知道它的公式如下: 


Li=Cmax(0,1yiwTxi)+R(W) 

我們可以理解爲類別yi1,1,它是我們的多類別識別的一個特殊情況,而這裏的C和λ是一樣的作用,只不過他們的大小對結果的影響是相反的,也就是C1λ

2.4.3 關於非線性的SVM

如果對機器學習有了解,你可能會了解很多其他關於SVM的術語:kernel,dual,SMO算法等等。在這個系列裏面我們只討論最基本的線性形式。當然,其實從本質上來說,這些方法都是類似的。

2.5 Softmax分類器

話說其實有兩種特別常見的分類器,前面提的SVM是其中的一種,而另外一種就是Softmax分類器,它有着截然不同的損失函數。如果你聽說過『邏輯迴歸二分類器』,那麼Softmax分類器是它泛化到多分類的情形。不像SVM這種直接給類目打分f(xi,W)並作爲輸出,Softmax分類器從新的角度做了不一樣的處理,我們依舊需要將輸入的像素向量映射爲得分f(x_i; W) = W x_i,只不過我們還需要將得分映射到概率域,我們也不再使用hinge loss了,而是使用交叉熵損失/cross-entropy loss,形式如下: 


Li=log(efyijefj)Li=fyi+logjefj 

我們使用fj來代表得分向量f的第j個元素值。和前面提到的一樣,總體的損失/loss也是Li遍歷訓練集之後的均值,再加上正則化項R(W),而函數fj(z)=ezjkezk被稱之爲softmax函數:它的輸入值是一個實數向量z,然後在指數域做了一個歸一化(以保證之和爲1)映射爲概率。

2.5.1 信息論角度的理解

對於兩個概率分佈p(“真實的概率分佈”)和估測的概率分佈q(估測的屬於每個類的概率),它們的互熵定義爲如下形式: 


H(p,q)=xp(x)logq(x) 

而Softmax分類器要做的事情,就是要最小化預測類別的概率分佈(之前看到了,是q=efyi/jefj)與『實際類別概率分佈』(p=[0,1,,0],只在結果類目上是1,其餘都爲0)兩個概率分佈的交叉熵

另外,因爲互熵可以用熵加上KL距離/Kullback-Leibler Divergence(也叫相對熵/Relative Entropy)來表示,即H(p,q)=H(p)+DKL(p||q),而p的熵爲0(這是一個確定事件,無隨機性),所以互熵最小化,等同於最小化兩個分佈之間的KL距離。換句話說,交叉熵想要從給定的分佈q上預測結果分佈p

2.5.2 概率角度的理解

我們再來看看以下表達式 


P(yixi;W)=efyijefj 

其實可以看做給定圖片數據xi和類別yi以及參數W之後的歸一化概率。在概率的角度理解,我們在做的事情,就是最小化錯誤類別的負log似然概率,也可以理解爲進行最大似然估計/Maximum Likelihood Estimation (MLE)。這個理解角度還有一個好處,這個時候我們的正則化項R(W)有很好的解釋性,可以理解爲整個損失函數在權重矩陣W上的一個高斯先驗,所以其實這時候是在做一個最大後驗估計/Maximum a posteriori (MAP)。

2.5.3 實際工程上的注意點:數據穩定性

在我們要寫代碼工程實現Softmax函數的時候,計算的中間項efyijefj因爲指數運算可能變得非常大,除法的結果非常不穩定,所以這裏需要一個小技巧。注意到,如果我們在分子分母前都乘以常數C,然後整理到指數上,我們會得到下面的公式: 


efyijefj=CefyiCjefj=efyi+logCjefj+logC 

C的取值由我們而定,不影響最後的結果,但是對於實際計算過程中的穩定性有很大的幫助。一個最常見的C取值爲logC=maxjfj。這表明我們應該平移向量f中的值使得最大值爲0,以下的代碼是它的一個實現:

<code class="language-python hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">f = np.array([<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">123</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">456</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">789</span>]) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 3個類別的預測示例</span>
p = np.exp(f) / np.sum(np.exp(f)) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 直接運算,數值穩定性不太好</span>

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 我們先對數據做一個平移,所以輸入的最大值爲0:</span>
f -= np.max(f) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># f 變成 [-666, -333, 0]</span>
p = np.exp(f) / np.sum(np.exp(f)) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># 結果正確,同時解決數值不穩定問題</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

2.5.4 關於softmax這個名字的一點說明

準確地說,SVM分類器使用hinge loss(有時候也叫max-margin loss)。而Softmax分類器使用交叉熵損失/cross-entropy loss。Softmax分類器從softmax函數(恩,其實做的事情就是把一列原始的類別得分歸一化到一列和爲1的正數表示概率)得到,softmax函數使得交叉熵損失可以用起來。而實際上,我們並沒有softmax loss這個概念,因爲softmax實質上就是一個函數,有時候我們圖方便,就隨口稱呼softmax loss。

2.6 SVM 與 Softmax

這個比較很有意思,就像在用到分類算法的時候,就會想SVM還是logistic regression呢一樣。 
我們先用一張圖來表示從輸入端到分類結果,SVM和Softmax都做了啥: 


SVM與SoftMax

區別就是拿到原始像素數據映射得到的得分之後的處理,而正因爲處理方式不同,我們定義不同的損失函數,有不同的優化方法。

2.6.1 另外的差別

  • SVM下,我們能完成類別的判定,但是實際上我們得到的類別得分,大小順序表示着所屬類別的排序,但是得分的絕對值大小並沒有特別明顯的物理含義
  • Softmax分類器中,結果的絕對值大小表徵屬於該類別的概率

舉個例子說,SVM可能拿到對應 貓/狗/船 的得分[12.5, 0.6, -23.0],同一個問題,Softmax分類器拿到[0.9, 0.09, 0.01]。這樣在SVM結果下我們只知道『貓』是正確答案,而在Softmax分類器的結果中,我們可以知道屬於每個類別的概率。

但是,Softmax中拿到的概率,其實和正則化參數λ有很大的關係,因爲λ實際上在控制着W的伸縮程度,所以也控制着最後得分的scale,這會直接影響最後概率向量中概率的『分散度』,比如說某個λ下,我們得到的得分和概率可能如下: 


[1,2,0][e1,e2,e0]=[2.71,0.14,1][0.7,0.04,0.26] 

而我們加大λ,提高其約束能力後,很可能得分變爲原來的一半大小,這時候如下: 


[0.5,1,0][e0.5,e1,e0]=[1.65,0.37,1][0.55,0.12,0.33] 

因爲λ的不同,使得最後得到的結果概率分散度有很大的差別。在上面的結果中,貓有着統治性的概率大小,而在下面的結果中,它和船隻的概率差距被縮小。

2.6.2 際應用中的SVM與Softmax分類器

實際應用中,兩類分類器的表現是相當的。當然,每個人都有自己的喜好和傾向性,習慣用某類分類器。

一定要對比一下的話:

SVM其實並不在乎每個類別得到的絕對得分大小,舉個例子說,我們現在對三個類別,算得的得分是[10, -2, 3],實際第一類是正確結果,而設定Δ=1,那麼10-3=7已經比1要大很多了,那對SVM而言,它覺得這已經是一個很標準的答案了,完全滿足要求了,不需要再做其他事情了,結果是 [10, -100, -100] 或者 [10, 9, 9],它都是滿意的。

然而對於Softmax而言,不是這樣的, [10, -100, -100] 和 [10, 9, 9]映射到概率域,計算得到的交叉熵損失是有很大差別的。所以Softmax是一個永遠不會滿足的分類器,在每個得分計算到的概率基礎上,它總是覺得可以讓概率分佈更接近標準結果一些,交叉熵損失更小一些。

有興趣的話,W與得分預測結果demo是一個可以手動調整和觀察二維數據上的分類問題,隨W變化結果變化的demo,可以動手調調看看。 


交互式demo 

參考資料與原文

cs231n 線性分類器 SVM與softmax

作者: 寒小陽 
時間:2015年11月。 
出處:http://blog.csdn.net/han_xiaoyang/article/details/49999299 
聲明:版權所有,轉載請註明出處,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章