天天看點

關于交叉熵(Cross Entropy)與Softmax目錄寫在前面一、KL散度二、交叉熵三、Softmax四、交叉熵與Softmax的關系五、交叉熵損失六、Softmax的求導七、堆疊+向量化八、其他關于Softmax函數的問題九、贈品

目錄

  1. 寫在前面
  2. KL散度
  3. 交叉熵
  4. Softmax
  5. 交叉熵與Softmax的關系
  6. 交叉熵損失
  7. Softmax的求導
  8. 堆疊+向量化
  9. 其他關于Softmax函數的問題
  10. 贈品

寫在前面

Softmax、交叉熵、交叉熵損失是機器學習與神經網絡模型的重要組成部分,一般來說,Softmax一般用于criterion(标準),交叉熵與交叉熵損失用來衡量模型預測與實際結果間的差别并産生用于反向傳播的梯度。

本文将基于這些專有名詞,粗略讨論其關系,并給出基于Softmax的交叉熵損失的求導過程。

一、KL散度

KL散度是資訊論中的重要概念,用來描述兩個機率分布的差異。

如果對于同一個随機變量 x x 有兩個單獨的機率分布P(x)P(x)和 Q(x) Q ( x ) ,則可以使用KL散度(Kullback-Leiber(KL) divergence)來衡量這兩個分布的差異:

DKL(P||Q)=Ex∼P[logP(x)Q(x)]=Ex∼P[logP(x)−logQ(x)] D K L ( P | | Q ) = E x ∼ P [ l o g P ( x ) Q ( x ) ] = E x ∼ P [ l o g P ( x ) − l o g Q ( x ) ]

KL散度有很多有用的性質:

  1. 最重要的是,它是非負的。
  2. 當 x x 是離散型變量時,KL散度為0當且僅當P(x)P(x)與 Q(x) Q ( x ) 具有相同的分布。
  3. KL散度表征了某種距離,但不是真正的距離,因為KL散度不對稱: DKL(P||Q)≠DKL(Q||P) D K L ( P | | Q ) ≠ D K L ( Q | | P )

二、交叉熵

交叉熵與KL散度密切相關:

H(P,Q)=H(P)+DKL(P||Q) H ( P , Q ) = H ( P ) + D K L ( P | | Q )

其中H(P,Q)是交叉熵(cross-entropy),H(P)是機率分布P的香農熵。

可以看出它與KL散度很像,但當我們将它展開後發現:

H(P,Q) H ( P , Q )

=H(P)+DKL(P||Q) = H ( P ) + D K L ( P | | Q )

=Ex∼P[logP(x)]+Ex∼P[logP(x)−logQ(x)] = E x ∼ P [ l o g P ( x ) ] + E x ∼ P [ l o g P ( x ) − l o g Q ( x ) ]

=−Ex∼PlogQ(x) = − E x ∼ P l o g Q ( x )

在深度學習中,針對Q最小化交叉熵等價于最小化KL散度,因為Q與P獨立,Q與P的香農熵無關。

在有監督學習的一般模型中,機率一般是離散的,是以交叉熵可以改寫為:

H(P,Q)=−∑xp(x)logq(x) H ( P , Q ) = − ∑ x p ( x ) l o g q ( x )

三、Softmax

下文将softmax函數記做 σ σ ,與二進制性的sigmoid函數保持一緻。

σ(z)i=ezi∑jezj σ ( z ) i = e z i ∑ j e z j

Softmax函數可以看做sigmoid函數的擴充,sigmoid函數一般用來表示二值型變量的分布,而Softmax可用來表示具有n個可能取值的離散型随機變量的分布。Softmax函數最常用作分類器的輸出。

易看出,Softmax函數具有如下性質:

  1. 歸一化, ∑iσ(z)i=1 ∑ i σ ( z ) i = 1
  2. 不改變原序列的排位
  3. 處處連續且可導,是一個性質良好的損失函數

舉例:

一個神經網絡的輸出層具有4個節點,其輸出的結果為

[1.2, 2.5, 0.1, 2.1]

,經過Softmax後的輸出為

[0.13, 0.49, 0.04, 0.33]

,此時最大的項仍然為第二項,是以說“不改變原序列的排位”。

Softmax函數的求導見下文。

四、交叉熵與Softmax的關系

事實上,交叉熵與Softmax沒有直接的關系。然而很多文章使用“交叉熵”特定表示伯努利或Softmax分布的負對數似然(NLL),這其實是用詞不當的。任何一個由負對數似然組成的損失都是定義在P與Q之間的交叉熵,在神經網絡模型中,P是訓練集上的經驗分布(真實資料),Q是模型上的機率分布(預測輸出)。

五、交叉熵損失

交叉熵的損失函數是一種代價函數(cost),代價函數在基于梯度的學習中扮演重要角色。

六、Softmax的求導

最激動人心的環節來了

符号使用:

  1. ŷ  y ^ 表示未歸一化的機率輸出
  2. y y 表示真實标簽,其中僅有一項為1,其餘均為0,為1的那項對應的下标便是真實标簽

σi=σ(y)i=eyi^∑nj=1eyj^σi=σ(y)i=eyi^∑j=1neyj^

使用交叉熵損失:

loss=−∑ni=1yilogσi l o s s = − ∑ i = 1 n y i l o g σ i

∂loss∂yk^=−∑ni=1yilogσiyk^ ∂ l o s s ∂ y k ^ = − ∑ i = 1 n y i l o g σ i y k ^

=∂(−y1logσ1−⋯−yklogσk−⋯−ynlogσn)∂yk^ = ∂ ( − y 1 l o g σ 1 − ⋯ − y k l o g σ k − ⋯ − y n l o g σ n ) ∂ y k ^

設 a≠b a ≠ b

∂yalogσa∂yb^=−yaeyb^∑ey=−yaσb ∂ y a l o g σ a ∂ y b ^ = − y a e y b ^ ∑ e y = − y a σ b

∂yblogσb∂yb^=yb∑ni=1,i≠beyi^∑ey=yb∑ey−eyb∑ey=yb(1−σb) ∂ y b l o g σ b ∂ y b ^ = y b ∑ i = 1 , i ≠ b n e y i ^ ∑ e y = y b ∑ e y − e y b ∑ e y = y b ( 1 − σ b )

帶回方程:

=y1σk+y2σk+⋯+yk−1σk+yk(σk−1)+yk+1σk+⋯+ynσk = y 1 σ k + y 2 σ k + ⋯ + y k − 1 σ k + y k ( σ k − 1 ) + y k + 1 σ k + ⋯ + y n σ k

=σk∑ni=1yi−yk = σ k ∑ i = 1 n y i − y k

由于y中僅有一項為1,其餘均為0

=σk−yk = σ k − y k

是以:

∂loss∂yk^=σk−yk ∂ l o s s ∂ y k ^ = σ k − y k

一般情況下, y y 不會被展開為01形式,而是直接代表對應的種類,我們隻需要按照實際情況稍作改動即可。

七、堆疊+向量化

很多時候我們看到有關矩陣的求導操作會一下子摸不到頭腦,實際上矩陣隻是把一條條向量堆疊起來後形成的具有統一形式的結果。

在這裡我們把第kk個樣本的輸出置為行向量,在樣本數為m,種類數為n的情況下,我們的輸出矩陣 A∈Rm∗n A ∈ R m ∗ n

當我們觀察某一個樣本時,由于 loss l o s s 為一個标量,而用來計算這個标量的是一個向量,是以在計算這個标量相對于這個向量的梯度時,實際是在計算一個雅可比(Jacobi)矩陣,從數學知識我們可以知道,這個矩陣的次元便是1*n。

然後,當我們把這m個1*n矩陣堆疊起來時,便得到了m*n的梯度矩陣。

八、其他關于Softmax函數的問題

像sigmoid函數一樣,Softmax激活函數也可能會飽和。當sigmoid的輸入值是正負極大時,梯度趨近于0,不利于學習。而對于Softmax,當輸入值之間的差異十分極端時,同樣會出現飽和。

可以觀察到,當對所有的輸入都加上同一個常數時,Softmax的輸出不變,因為輸入資料的排位不變:

softmax(z)=softmax(z+c) s o f t m a x ( z ) = s o f t m a x ( z + c )

利用這一性質,我們可以導出一個相對穩定的Softmax函數的變體:

softmax(z)=softmax(z−max{zi}) s o f t m a x ( z ) = s o f t m a x ( z − m a x { z i } )

雖然此時在資料呈集團分布且不同集團間差距非常大時仍會出現飽和,但其實際表現性能已經足夠好。

九、贈品

class Softmax(Layer):
    def forward(self, x, y):
        self.x = (x.copy() - x.max(axis=).reshape(-, ))
        self.y = y.copy()
        self.m, self.n = self.x.shape
        self.denom = np.sum(np.exp(x), axis=).reshape((-, ))
        self.softmax = np.exp(x) / self.denom
        loss = 
        for i in range(self.m):
            loss -= np.log(self.softmax[i, y[i]])
        return loss / self.m

    def dirac(self, a, b):
        return  if a == b else 

    def backprop(self):
        grad = np.zeros([self.m, self.n])
        for i in range(self.m):
            for j in range(self.n):
                grad[i, j] = (self.softmax[i, j] -  
                              self.dirac(j, self.y[i])) / self.m
        return grad
           

(全文完 2018.8.20)

繼續閱讀