我有一个softmax层(只有激活本身,而没有权重乘以输入的线性部分),我想为此做一个后向传递。
我找到了许多有关SO的教程/答案,但是它们似乎都使用X
作为(1, n_inputs)
的载体。我想将其用作(n_samples, n_inputs)
数组,并且仍然对前进/后退传递具有正确的矢量化实现。
我编写了以下正向传递,将每行/每个样本的输出归一化(对吗?):
import numpy as np
X = np.asarray([
[0.0, 0.0],
[0.0, 1.0],
[1.0, 0.0],
[1.0, 1.0]], dtype=np.float32)
def prop(self, X):
s = np.exp(X)
s = s.T / np.sum(s, axis=1)
return s.T
它给我的前向传播(包括其他层)的最终结果为:
Y = np.asarray([
[0.5 , 0.5 ],
[0.87070241, 0.12929759],
[0.97738616, 0.02261384],
[0.99200957, 0.00799043]], dtype=np.float32))
因此,这是softmax的输出(如果正确)。现在,我应该如何编写后退密码?
我得出softmax的导数为:
1)如果i=j
:p_i*(1 - p_j)
,
2)如果i!=j
:-p_i*p_j
,
我试图将导数计算为:
ds = np.diag(Y.flatten()) - np.outer(Y, Y)
但是它会导致8x8矩阵,这对于随后的反向传播是没有意义的。正确的写法是什么?
答案 0 :(得分:1)
我一直在处理相同的问题,并最终想出了一种向量化softmax Jacobian批处理实现的方法。我本人想出了它,所以我不确定这是否是最佳方法。这是我的主意:
import numpy as np
from scipy.special import softmax
def Jsoftmax(X):
sh = X.shape
sm = softmax(X, axis = 1)
DM = sm.reshape(sh[0],-1,1) * np.diag(np.ones(sh[1])) # Diagonal matrices
OP = np.matmul(sm.reshape(sh[0],-1,1), sm.reshape(sh[0],1,-1)) # Outer products
Jsm = DM - OP
return Jsm
它会生成一个(n_samples, n_inputs, n_inputs)
形的数组,我认为该数组可以与np.matmul
函数一起反向传播,以正确地乘以dJ_dA
数组。
应注意,softmax几乎专门用作最后一层,通常具有交叉吸收损耗目标函数。在这种情况下,可以更有效地找到目标函数相对于softmax输入的导数为(S - Y)/m
,其中m
是批次中的示例数,Y
是您的批次的标签和S
是您的softmax输出。以下link对此进行了说明。
答案 1 :(得分:0)
在编写softmax函数Softmax derivative in NumPy approaches 0 (implementation)时,我发现此问题非常有用。希望对您有所帮助。