現在,有一個分類問題:
feature是2維的向量
目標類別有3種
一共有4個樣本:
我們準備用一個只有一層的全連線神經網路來解決這個問題(使用多層神經網路推導太複雜,並且不利於理解,多層神經網路不是講清此問題的關鍵)。
首先,我們需要使用one-hot來表示目標類別(為什麼使用one-hot是另一個問題),所以,全連線神經網路的最後一層有三個數字。
哎呀!樣本數太多了寫起來很複雜,在計算loss時,只是簡單地對多個樣本產生的損失求均值,所以下面我們改一下問題:假設只有一個樣本,這個樣本的特徵為
,這個樣本經過全連線神經網路之後輸出為:
,真實值為
。
不妨設這個樣本的類別為1,它的one-hot真實向量為(1,0,0)。
其實,在最後一層輸出的時候,我們需要使用softmax把
進行歸一化。softmax的過程此處就省略了,
就當#FormatImgID_7#已經是softmax之後的結果了吧(因為softmax不是解釋此問題的關鍵)
。
下面看平方誤差:
再看交叉熵誤差:
其中,
表示真實值,
表示預測值。三部分是完全相同的,它們反向傳播時效果是相似的。所以,我們只分析
和
對權值的影響,和
有關的三個權值是
,別的權值不用看。我們只分析損失z對
的影響。
對於平方誤差:
我們想知道的是什麼?我們想知道的是
。也就是
調整的幅度和絕對誤差
之間的關係。
記絕對誤差
因為我們使用了one-hot,所以
的真實值只能取0和1,而one-hot之後
的值必然在0到1之間。
當
時,
,代入
得到
當
時
,
此式中,
是常量,不必關心,我們只看
的形狀
這個函式長啥樣子?
import
matplotlib。pyplot
as
plt
import
numpy
as
np
A
=
np
。
linspace
(
0
,
1
,
100
)
plt
。
plot
(
A
,
A
**
2
*
(
1
-
A
))
plt
。
xlabel
(
“absolute error”
)
plt
。
ylabel
(
“$\delta w_
{11}
$”
)
plt
。
title
(
“$\delta w_
{11}
$=f(A)”
)
plt
。
show
()
隨著絕對誤差的增大,權值需要調整的幅度先變大後變小,這就導致當絕對誤差很大時,模型顯得“自暴自棄”不肯學習
隨著絕對誤差的增大,權值需要調整的幅度先變大後變小,這就導致當絕對誤差很大時,模型顯得“自暴自棄”不肯學習
對於交叉熵誤差:
可以看到,使用交叉熵之後,絕對誤差和需要調整的幅度成正比。
我們回過頭來比較平方損失和交叉熵損失的區別,會發現:
平方損失的“罪魁禍首”是sigmoid函式求導之後變成
,平白無故讓曲線變得非常複雜,如果前面能夠產生一個
把後面多餘項“吃掉”多好
交叉熵的優勢就是:它求導之後只提供了一個
去中和後面的導數。
以上都只是理論推導,那麼實際上到底是不是這麼回事呢?我們可以做個實驗:
,其中x=1,y=1。w始終在變化,b始終固定為0。2。在w變化過程中,我們記錄下絕對誤差和w的梯度。
import
matplotlib。pyplot
as
plt
import
numpy
as
np
import
tensorflow
as
tf
x
=
tf
。
placeholder
(
dtype
=
tf
。
float32
,
shape
=
(),
name
=
“x”
)
y
=
tf
。
placeholder
(
dtype
=
tf
。
float32
,
shape
=
(),
name
=
“y”
)
w
=
tf
。
Variable
(
0。8
)
b
=
tf
。
Variable
(
0。2
)
yy
=
tf
。
sigmoid
(
w
*
x
+
b
)
cross
=
-
y
*
tf
。
log
(
yy
)
mse
=
(
yy
-
y
)
**
2
cross_grad
=
tf
。
gradients
(
cross
,
[
w
,
b
])
mse_grad
=
tf
。
gradients
(
mse
,
[
w
,
b
])
abs_error
=
tf
。
abs
(
y
-
yy
)
with
tf
。
Session
()
as
sess
:
sess
。
run
(
tf
。
global_variables_initializer
())
w_value_list
=
np
。
linspace
(
-
8
,
8
,
100
)
cross_grad_w_list
,
mse_grad_w_list
,
abs_error_list
=
[],
[],
[]
for
w_value
in
w_value_list
:
sess
。
run
(
tf
。
assign
(
w
,
w_value
))
abs_error_value
,
(
cross_grad_w
,
_
),
(
mse_grad_w
,
_
)
=
sess
。
run
([
abs_error
,
cross_grad
,
mse_grad
],
feed_dict
=
{
x
:
1
,
y
:
1
})
cross_grad_w_list
。
append
(
cross_grad_w
)
mse_grad_w_list
。
append
(
mse_grad_w
)
abs_error_list
。
append
(
abs_error_value
)
plt
。
plot
(
abs_error_list
,
cross_grad_w_list
,
label
=
“cross_w=f(A)”
)
plt
。
plot
(
abs_error_list
,
mse_grad_w_list
,
label
=
“mse_w=f(A)”
)
plt
。
title
(
“why do we use cross_entropy?”
)
plt
。
legend
()
plt
。
show
()
實驗證明,理論推導是正確的:交叉熵使得梯度與絕對誤差成正比,二範數導致梯度變得扭曲
參考資料