javascript
KL散度、交叉熵与JS散度数学公式以及代码例子
KL散度、交叉熵與JS散度數學公式以及代碼例子
1.1 KL 散度概述
KL 散度 ,Kullback-Leibler divergence,(也稱相對熵,relative entropy)是概率論和信息論中十分重要的一個概念,是兩個概率分布(probability distribution)間差異的非對稱性度量。
對離散概率分布的 KL 散度 計算公式為:
KL(p∣∣q)=∑p(x)log?p(x)q(x)(1)KL(p||q)=\sum p(x)\log{{p(x)}\over{q(x)}} \tag{1} KL(p∣∣q)=∑p(x)logq(x)p(x)?(1)
對連續概率分布的 KL 散度 計算公式為:
KL(p∣∣q)=∫p(x)log?p(x)q(x)dx(2)KL(p||q)=\int p(x)\log {{p(x)}\over{q(x)}}dx \tag{2} KL(p∣∣q)=∫p(x)logq(x)p(x)?dx(2)
一般情況下,我們得到的數據都是離散的。
KL 散度 的重要性質:
- KL 散度 的結果是非負的。
- KL 散度 是非對稱的,即 KL(p∣∣q)!=KL(q∣∣p)KL(p||q) \ !=\ KL(q||p)KL(p∣∣q)?!=?KL(q∣∣p)
具體內容參考 [1] 《深度學習輕松學》。
1.2 KL 散度計算方法的代碼實現
1.2.1 自己編寫代碼
請結合 公式 (1) 理解以下代碼:
import numpy as np import mathdef KL(p,q):# p,q 為兩個 list,表示對應取值的概率 且 sum(p) == 1 ,sum(q) == 1return sum(_p*math.log(_p/_q) for (_p,_q) in zip(p,q) if _p != 0 )P = [0.2, 0.4, 0.4] Q = [0.4, 0.2, 0.4]print(KL(P,Q))輸出內容為:
0.13862943611198905計算過程:KL(P,Q)=0.2?log?0.20.4+0.4?log?0.40.2+0.4?log?0.40.4=0.13862943611198905KL(P,Q) = 0.2*\log{{0.2}\over{0.4}}+0.4*\log{{0.4}\over{0.2}}+0.4*\log{{0.4}\over{0.4}}=0.13862943611198905KL(P,Q)=0.2?log0.40.2?+0.4?log0.20.4?+0.4?log0.40.4?=0.13862943611198905
當然如果是 二分類問題 計算過程也是一樣的。
import numpy as np import mathdef KL(p,q):# p,q 為兩個 list,表示對應取值的概率 且 sum(p) == 1 ,sum(q) == 1return sum(_p*math.log(_p/_q) for (_p,_q) in zip(p,q) if _p != 0 )P = [0, 1] Q = [0.4,0.6]print(KL(P,Q))輸出結果為:
0.5108256237659907可以自己復制粘貼填寫更多測試數值,需要 保證:
- P 中每一組的各項的和為 1
- Q 中每一組各項的和為 1
- Q 中每一組不允許出現 0
注意: 以上內容只考慮維度為 1 的情況,如 P=[[0.1,0.2,0.7],[0.2,0.3,0.5]] 這種情況沒有考慮到。
1.2.2 使用已存在的庫 scipy
from scipy import statsP = [0.2, 0.4, 0.4] Q = [0.4, 0.2, 0.4] stats.entropy(P,Q)輸出內容為:
0.13862943611198905和上面的例子一樣,可以測試那些數據。
2.1 交叉熵基本概述
交叉熵(Cross Entropy)是 Shannon 信息論中一個重要概念,主要用于度量兩個概率分布間的差異性信息。
對 二分類 任務的 交叉熵 計算公式為:
CrossEntropy(y,y^)=?(y?log?(y^)+(1?y)?log?(1?y^))(3)Cross Entropy\ (y, {\hat y}) = -(y*\log(\hat y)+(1-y)*\log(1-\hat y)) \tag{3} CrossEntropy?(y,y^?)=?(y?log(y^?)+(1?y)?log(1?y^?))(3)
其中,yyy 可以理解為數據集中的 label,也就是接下來例子中的 ytruey_{true}ytrue?;而 y^\hat yy^? 可以理解與模型的預測標簽,也就是接下來的例子中的 ypredy_{pred}ypred?。
注意: 如果二分類問題時,yyy 的取值并非 [0,1] 兩種這種正常模式,需要分別求和再求均值,這一部分內容將會在后面 2.3 進行討論。
對 多分類 任務的交叉熵計算公式為:
CrossEntropy(y,y^)=∑y?log?(1y^)(4)Cross Entropy\ (y, {\hat y}) = \sum y*\log ({{1}\over{\hat y}}) \tag{4} CrossEntropy?(y,y^?)=∑y?log(y^?1?)(4)
公式 4 可以寫成如下格式:
CrossEntropy(y,y^)=?∑y?log?(y^)(5)Cross Entropy\ (y, {\hat y}) = -\sum y*\log ({\hat y}) \tag{5} CrossEntropy?(y,y^?)=?∑y?log(y^?)(5)
其中, 多分類 任務的計算公式同樣適用于 二分類 ,因為二分類任務直接等于兩個概率運算的和,所以沒必要加 ∑\sum∑ 符號。
2.2 交叉熵計算方法的代碼實現
2.2.1 自己編寫代碼
參考 公式4 ,編寫代碼比較簡單。
import mathdef CE(p,q):# p,q 為兩個 list,表示對應取值的概率 且 sum(p) == 1 ,sum(q) == 1return sum(_p*math.log( 1 /_q) for (_p,_q) in zip(p,q) if _q != 0 )P = [0.,1.] Q = [0.6,0.4]print(CE(P,Q))計算過程:CE(P,Q)=0?log?(10.6)+1?log?(10.4)=log?(2.5)=0.91629073187415506518352721176801CE(P, Q)=0*\log({{1}\over{0.6}})+1*\log({{1}\over{0.4}})=\log(2.5)=0.91629073187415506518352721176801CE(P,Q)=0?log(0.61?)+1?log(0.41?)=log(2.5)=0.91629073187415506518352721176801
輸出內容為:
0.9162907318741551注意對比前文中對 KL 的實現的代碼,非常相似,除了函數名就改動兩個地方。
根據 公式5,代碼實現如下:
import mathdef CE(p,q):# p,q 為兩個 list,表示對應取值的概率 且 sum(p) == 1 ,sum(q) == 1return -sum(_p*math.log(_q) for (_p,_q) in zip(p,q) if _q != 0 )P = [0.,1.] Q = [0.6,0.4]print(CE(P,Q))輸出結果為:
0.9162907318741552.2.1 使用 tensorflow2
主要是因為使用 tensorflow 的時候可能會用到這個函數,所以特地在這里介紹一下計算過程。
注意 這里只適合 Binary 這種情況,也就是說標簽只是 0 與 1 兩種。具體參考 [3]
這里直接上例子,然后解釋計算過程:
import tensorflow as tfy_true = [0., 1.] y_pred = [0.6, 0.4] bce = tf.keras.losses.BinaryCrossentropy() bce(y_true, y_pred).numpy()輸出內容為:
0.9162905在官方文檔中給出的例子有兩組數據,代碼如下:
import tensorflow as tfy_true = [[0., 1.], [0., 1.]] y_pred = [[0.6, 0.4], [0.4, 0.6]] # Using 'auto'/'sum_over_batch_size' reduction type. bce = tf.keras.losses.BinaryCrossentropy() bce(y_true, y_pred).numpy()輸出內容為:
0.71355796這種情況只是分別求兩組結果,再進行平均即可。
2.3 當測試數據為全0或全1 的二分類時
上面的公式以及自己編寫的代碼中,都沒有考慮到當測試數據為全0 或全 1 這種情況,接下來在這里討論,在二分類問題中,應該如何計算結果。
CrossEntropy(y,y^)=?(y?log?(y^)+(1?y)?log?(1?y^))(3)Cross Entropy\ (y, {\hat y}) = -(y*\log(\hat y)+(1-y)*\log(1-\hat y)) \tag{3} CrossEntropy?(y,y^?)=?(y?log(y^?)+(1?y)?log(1?y^?))(3)
如 公式3 所示,在 輸入數據的 yyy 為 [0,1] 兩種時,使用 公式3,4,5 計算都可以。但是如果輸入數據的 yyy 全部為 0 的時候,公式3 與公式4 則不適用。
這個時候需要使用 公式3 進行計算,并且同樣需要做一次求均值,如 公式6 所示。
CrossEntropy(y,y^)=?1n∑i=1n(yi?log?(y^i)+(1?yi)?log?(1?y^i))(6)Cross Entropy\ (y, {\hat y}) = -{{1}\over{n}}\sum_{i=1}^n(y_i*\log(\hat y_i)+(1-y_i)*\log(1-\hat y_i)) \tag{6} CrossEntropy?(y,y^?)=?n1?i=1∑n?(yi??log(y^?i?)+(1?yi?)?log(1?y^?i?))(6)
對應的代碼實現如下:
import mathdef CE(p,q):return -sum((_p*math.log(_q)+(1-_p)*math.log(1-_q) )for (_p,_q) in zip(p,q) if _q != 0 )/len(p)P = [0.,0.] Q = [0.6,0.4]print(CE(P,Q))輸出結果為:
0.7135581778200728計算過程:CE(p,q)=?12((0?log?0.6+1?log?0.4)+(0?log?0.4+1?log?0.6))=?12log?0.24=0.71355817782007287419452065403584CE(p,q)=-{{1}\over{2}}((0*\log 0.6 + 1*\log 0.4)+(0*\log 0.4+1*\log0.6))=-{{1}\over{2}}\log0.24=0.71355817782007287419452065403584CE(p,q)=?21?((0?log0.6+1?log0.4)+(0?log0.4+1?log0.6))=?21?log0.24=0.71355817782007287419452065403584
對應的 tensorflow2 代碼如下:
import tensorflow as tfy_true = [0., 0.] y_pred = [0.4, 0.6]bce = tf.keras.losses.BinaryCrossentropy() bce(y_true, y_pred).numpy()輸出結果為:
0.71355796如果測試數據為 [1.,1.] 的時候,輸出結果是相同的。
3.1 JS 散度概述
JS 散度 Jensen-Shannon divergence 用于描述兩個概率分布的相似程度。和上面的 KL 的描述一致的話,JS 散度是兩個概率分布間差異的對稱性度量。
JS 散度的求解公式如下:
JS(P∣∣Q)=12KL(P∣∣P+Q2)+12KL(Q∣∣P+Q2)(7)JS(P \ ||\ Q ) = {{1}\over{2}}KL(P\ ||\ {{P+Q}\over{2}})+{{1}\over{2}}KL(Q \ ||\ {{P+Q}\over{2}}) \tag{7} JS(P?∣∣?Q)=21?KL(P?∣∣?2P+Q?)+21?KL(Q?∣∣?2P+Q?)(7)
很明顯,等式是對稱成立的,也就是說 JS(P∣∣Q)==JS(Q∣∣P)JS(P \ || \ Q) == JS(Q\ ||\ P)JS(P?∣∣?Q)==JS(Q?∣∣?P)
3.2 JS散度計算方法的代碼實現
公式7 是通過計算 KL 散度來計算 JS 散度的,因此代碼實現需要用到前面的 KL 散度,具體實現如下:
實驗 1
import mathdef KL(p,q):# p,q 為兩個 list,表示對應取值的概率 且 sum(p) == 1 ,sum(q) == 1return sum(_p*math.log(_p/_q) for (_p,_q) in zip(p,q) if _p != 0 )def JS(p,q):M = [0.5*(_p +_q) for (_p,_q) in zip(p,q)]return 0.5*(KL(p,M)+KL(q,M))P = [0.,1.] Q = [0.6,0.4]print(JS(P,Q)) print(JS(Q,P))輸出內容為:
0.27435846855026524 0.27435846855026524可以看出 JS(P,Q)JS(P,Q)JS(P,Q) 與 JS(Q,P)JS(Q,P)JS(Q,P) 相等。
實驗 2
接下來看一下兩個概率分布差異大小在 JS 值上的直觀反映:
import mathdef KL(p,q):# p,q 為兩個 list,表示對應取值的概率 且 sum(p) == 1 ,sum(q) == 1return sum(_p*math.log(_p/_q) for (_p,_q) in zip(p,q) if _p != 0 )def JS(p,q):M = [0.5*(_p +_q) for (_p,_q) in zip(p,q)]return 0.5*(KL(p,M)+KL(q,M))P = [0.,1.] Q = [0.01,0.99]print(JS(P,Q))P = [0.,1.] Q = [0.1,0.9]print(JS(P,Q))P = [0.,1.] Q = [0.5,0.5]print(JS(P,Q))P = [0.,1.] Q = [0.9,0.1]print(JS(P,Q))P = [0.,1.] Q = [0.99,0.01]print(JS(P,Q))輸出內容如下:
0.003478298769743019 0.03597375665014844 0.21576155433883565 0.5255973270178643 0.665096412549155可以看出,最開始的數據兩個概率分布是非常接近的,因為 JS 值比較小,接著兩個概率分布差異越來越多,JS 值也越來越大。
實驗 3
接著使用相同的數據,測試一下 KL 值的大小與概率分布的關系,因為 KL 計算是非對稱的,因此每次需要輸出兩個結果。
import mathdef KL(p,q):# p,q 為兩個 list,表示對應取值的概率 且 sum(p) == 1 ,sum(q) == 1return sum(_p*math.log(_p/_q) for (_p,_q) in zip(p,q) if _p != 0 )P = [0.1,0.9] Q = [0.01,0.99]print(KL(P,Q),KL(Q,P))P = [0.1,0.9] Q = [0.1,0.9]print(KL(P,Q),KL(Q,P))P = [0.1,0.9] Q = [0.5,0.5]print(KL(P,Q),KL(Q,P))P = [0.1,0.9] Q = [0.9,0.1]print(KL(P,Q),KL(Q,P))P = [0.1,0.9] Q = [0.99,0.01]print(KL(P,Q),KL(Q,P))輸出結果為:
0.14447934747551233 0.07133122707634103 0.0 0.0 0.3680642071684971 0.5108256237659907 1.7577796618689758 1.7577796618689758 3.8205752275831846 2.224611312865836可以看出,當 P 和 Q 相同分布時,KL 散度為 0;KL 散度隨著分布差異的增大而增大,隨著分布差異的減小而減小。
小結
根據以上實驗,可以看出:
- 隨著兩個概率分布差異的增大,KL 散度與 JS 散度的數值將增大;反之亦然。
- 隨著兩個概率分布差異的增大,KL 散度的增大時不均勻的,而 JS 散度的增大時均勻的。
- KL 散度的不對稱性可能帶來一些潛在的問題。
4. 總結
本文總結了 KL 散度、交叉熵以及 JS 散度數學公式以及一些性質,并且通過 python 代碼實現。在閱讀論文或實際編碼中,如果忘記了這方面的內容,可以考慮參考一下。
如有任何疑問,歡迎留言評論!
感謝您的閱讀!如果有幫助到您的話,歡迎點贊 + 關注 !感謝!
Smileyan
2021.3.28 22:15
[1] 《深度學習輕松學》核心算法與視覺實踐 馮超著 電子工業出版社 P33
[2] 《相對熵(KL散度)計算過程》
[3] tensorlflow文檔 tf.keras.metrics.BinaryCrossentropy
[4] 《熵與信息增益》
[5] 百度百科 相對熵 KL 散度
[6] 百度百科 交叉熵
[7] 熵與信息增益
[8] 二分類、多分類交叉熵的計算
[9] tensorflow BinaryCrossentropy 源碼
[10] 知乎 為什么交叉熵(cross-entropy)可以用于計算代價?
[11] 交叉熵、相對熵(KL散度)、JS散度和Wasserstein距離(推土機距離)
[12] tensorflow官網 KL公式
總結
以上是生活随笔為你收集整理的KL散度、交叉熵与JS散度数学公式以及代码例子的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 仿宋GB_2312字体在wps,offi
- 下一篇: html怎么命令打开文件夹下,dos下打