[英]numpy - why numerical gradient log(1-sigmoid(x)) diverges but log(sigmoid(x)) does not?
為什么邏輯對數損失 function f(x) = -np.log(1.0 - __sigmoid(x))
的數值梯度(f(x+k)-f(xk)) / 2k
發散但-np.log(__sigmoid(x))
沒有? 潛在的案例和機制是什么,還是我犯了錯誤? 代碼在底部。
任何關於如何實現數字梯度的建議、更正、見解、資源參考或建議/提示/提示將不勝感激。
嘗試實現邏輯對數損失 function 的數值梯度(f(x+k)-f(xk)) / 2k
。 圖中的y
是二進制真/假 label T
和p
是激活sigmoid(x)
。
當k
相對較大時,例如1e-5
,問題不會發生,至少在x
的范圍內。
然而,當k
變小時,例如1e-08
, -np.log(1.0 - __sigmoid(x))
開始發散。 但是,它不會發生在-np.log(__sigmoid(x))
上。
想知道減去1.0 - sigmoid(x)
是否與浮點數在計算機中以二進制方式存儲和計算的方式有關。
試圖使k
更小的原因是通過添加一個小數u
例如1e-5
來防止log(0)
變為np.inf
,但是log(x+1e-5)
會導致數值梯度與解析梯度的偏差。 為了將影響降到最低,我嘗試將其降到最低並開始遇到此問題。
import numpy as np
import inspect
from itertools import product
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
def __sigmoid(X):
return 1 / (1 + np.exp(-1 * X))
def __logistic_log_loss(X: np.ndarray, T: np.ndarray):
return -(T * np.log(__sigmoid(X)) + (1-T) * np.log(1-__sigmoid(X)))
def __logistic_log_loss_gradient(X, T):
Z = __sigmoid(X)
return Z-T
N = 1000
left=-20
right=20
X = np.linspace(left,right,N)
T0 = np.zeros(N)
T1 = np.ones(N)
# --------------------------------------------------------------------------------
# T = 1
# --------------------------------------------------------------------------------
fig, ax = plt.subplots(figsize=(8,6))
ax.plot(
X,
__logistic_log_loss(X, T1),
color='blue', linestyle='solid',
label="logistic_log_loss(X, T=1)"
)
ax.plot(
X,
__logistic_log_loss_gradient(X, T1),
color='navy', linestyle='dashed',
label="Analytical gradient(T=1)"
)
# --------------------------------------------------------------------------------
# T = 0
# --------------------------------------------------------------------------------
ax.plot(
X,
__logistic_log_loss(X, T0),
color='magenta', linestyle='solid',
label="logistic_log_loss(X, T=0)"
)
ax.plot(
X,
__logistic_log_loss_gradient(X, T0),
color='purple', linestyle='dashed',
label="Analytical gradient(T=0)"
)
ax.set_xlabel("X")
ax.set_ylabel("dL/dX")
ax.set_title("Logistic log loss and gradient")
ax.legend()
ax.grid(True)
def t_0_loss(X):
return [
#logistic_log_loss(P=sigmoid(x), T=0)
-np.log(1.0 - __sigmoid(x)) for x in X
]
def t_1_loss(X):
return [
#logistic_log_loss(P=sigmoid(x), T=1)
-np.log(__sigmoid(x)) for x in X
]
N = 1000
left=-1
right=15
# Numerical gradient
# (f(x+k)-f(x-k)) / 2k
k = 1e-9
X = np.linspace(left,right,N)
fig, axes = plt.subplots(1, 2, figsize=(10,8))
# --------------------------------------------------------------------------------
# T = 0
# --------------------------------------------------------------------------------
axes[0].plot(
X,
((np.array(t_0_loss(X + k)) - np.array(t_0_loss(X - k))) / (2*k)),
color='red', linestyle='solid',
label="Diffed numerical gradient(T=0)"
)
axes[0].plot(
X[0:-1:20],
((np.array(t_0_loss(X + k)) - np.array(t_0_loss(X))) / k)[0:-1:20],
color='black', linestyle='dotted', marker='x', markersize=4,
label="Left numerical gradient(T=0)"
)
axes[0].plot(
X[0:-1:20],
((np.array(t_0_loss(X)) - np.array(t_0_loss(X - k))) / k)[0:-1:20],
color='salmon', linestyle='dotted', marker='o', markersize=5,
label="Right numerical gradient(T=0)"
)
axes[0].set_xlabel("X")
axes[0].set_ylabel("dL/dX")
axes[0].set_title("T=0: -log(1-sigmoid(x))")
axes[0].legend()
axes[0].grid(True)
# --------------------------------------------------------------------------------
# T = 1
# --------------------------------------------------------------------------------
axes[1].plot(
X,
((np.array(t_1_loss(X + k)) - np.array(t_1_loss(X - k))) / (2*k)),
color='blue', linestyle='solid',
label="Diffed numerical gradient(T=1)"
)
axes[1].plot(
X[0:-1:20],
((np.array(t_1_loss(X + k)) - np.array(t_1_loss(X))) / k)[0:-1:20],
color='cyan', linestyle='dashed', marker='x', markersize=5,
label="Left numerical gradient(T=1)"
)
axes[1].plot(
X[0:-1:20],
((np.array(t_1_loss(X)) - np.array(t_1_loss(X - k))) / k)[0:-1:20],
color='yellow', linestyle='dotted', marker='o', markersize=5,
label="Right numerical gradient(T=1)"
)
axes[1].set_xlabel("X")
axes[1].set_ylabel("dL/dX")
axes[1].set_title("T=1: -log(sigmoid(x)")
axes[1].legend()
axes[1].grid(True)
每當將實數轉換為(或計算為)任何有限精度的數字格式時,都可能存在一定的誤差。 假設在特定區間內,數值格式能夠以P中的一部分精度表示值。 換句話說,格式中可表示的數字出現在相對於數字大小相距約 1/ P的距離處。
然后,當將實數轉換為可表示數時,如果我們選擇最接近的可表示數,則誤差(忽略符號)最多可能為 1/ P的 ½(相對於幅度)。 如果實數恰好在或接近可表示的數字,它可能會更小。
現在考慮您的表達式f(x+k)-f(xk)
。 f(x+k)
和f(xk)
在 1/ P的 ¼ 左右會有一些誤差,如果它們是多次計算的結果,可能會更大,如果幸運的話,可能會更少。 但是,對於一個簡單的 model,我們可以計算出錯誤將在 1/ P區域的某個地方。 當我們減去它們時,誤差可能仍然在 1/ P范圍內。 f(x+k)
和f(xk)
中的誤差可能會在減法中加強或抵消,所以有時你會得到非常小的總誤差,但它通常會在 1/ P左右。
在您的情況下, f(x+k)
和f(xk)
非常接近。 因此,當它們被減去時,結果的大小要比它們小得多。 大約 1/ P的誤差與f(x+k)
和f(xk)
的大小有關。 由於f(x+k)-f(xk)
與f(x+k)
和f(xk)
相比非常小,因此 1/ P相對於f(x+k)
和f(xk)
) 的誤差很大相對於f(x+k)-f(xk)
更大。
這是圖表中大部分噪音的來源。
為避免這種情況,您需要更精確地計算f(x+k)-f(xk)
,或者您需要避免該計算。
Reza.B. 的解決方案
令 z=1/(1+p), p= e^(-x)。 然后你可以看到 log(1-z)=log(p)-log(1+p),它在舍入誤差方面更穩定(我們擺脫了除法,這是數值不穩定性的主要問題)。
錯誤已解決。
def t_0_loss(X):
L = X + np.log(1 + np.exp(-X))
return L.tolist()
def t_1_loss(X):
L = np.log(1 + np.exp(-X))
return L.tolist()
%%timeit
((np.array(t_0_loss(X + k)) - np.array(t_0_loss(X - k))) / (2*k))
((np.array(t_1_loss(X + k)) - np.array(t_1_loss(X - k))) / (2*k))
---
599 µs ± 65.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
以前的錯誤版本是:
47 ms ± 617 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.