向量化softmax梯度

时间:2019-12-11 13:30:00

标签: python numpy backpropagation

我有一个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=jp_i*(1 - p_j)

2)如果i!=j-p_i*p_j

其中equation

我试图将导数计算为:

ds = np.diag(Y.flatten()) - np.outer(Y, Y) 

但是它会导致8x8矩阵,这对于随后的反向传播是没有意义的。正确的写法是什么?

2 个答案:

答案 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)时,我发现此问题非常有用。希望对您有所帮助。