NumPy中的Softmax导数接近0(实现)

时间:2016-03-29 09:07:57

标签: python numpy neural-network softmax

我试图为Numpy编写的神经网络实现softmax函数。设 h 为给定信号 i 的softmax值。

softmax function

我很难实现softmax激活功能的偏导数。

the softmax partial derivative

我目前遇到的问题是,随着培训的进行,所有偏导数都接近0。我用this excellent answer交叉引用了我的数学,但我的数学似乎没有成功。

import numpy as np
def softmax_function( signal, derivative=False ):
    # Calculate activation signal
    e_x = np.exp( signal )
    signal = e_x / np.sum( e_x, axis = 1, keepdims = True )

    if derivative:
        # Return the partial derivation of the activation function
        return np.multiply( signal, 1 - signal ) + sum(
            # handle the off-diagonal values
            - signal * np.roll( signal, i, axis = 1 )
            for i in xrange(1, signal.shape[1] )
        )
    else:
        # Return the activation signal
        return signal
#end activation function

signal参数包含发送到激活函数的输入信号,其形状为(n_samples,n_features)

# sample signal (3 samples, 3 features)
signal = [[0.3394572666491664, 0.3089068053925853, 0.3516359279582483], [0.33932706934615525, 0.3094755563319447, 0.3511973743219001], [0.3394407172182317, 0.30889042266755573, 0.35166886011421256]]

以下代码剪辑是一个完全正常工作的激活函数,仅作为参考和证明(主要是我自己),概念性实际上有效。

from scipy.special import expit
import numpy as np
def sigmoid_function( signal, derivative=False ):
    # Prevent overflow.
    signal = np.clip( signal, -500, 500 )

    # Calculate activation signal
    signal = expit( signal )

    if derivative:
        # Return the partial derivation of the activation function
        return np.multiply(signal, 1 - signal)
    else:
        # Return the activation signal
        return signal
#end activation function

修改

  • 简单的单层网络直观地存在问题。 softmax(及其衍生物)应用于最后一层。

1 个答案:

答案 0 :(得分:9)

这是如何以更加矢量化的numpy方式计算softmax函数的导数的答案。然而,偏导数逼近零的事实可能不是数学问题,而只是学习速率或已知的垂死权重问题与复杂的深度神经网络的问题。像ReLU这样的图层有助于防止后一问题。

首先,我使用了以下信号(只是复制了您的上一个条目),使其成为4 samples x 3 features,因此更容易看到尺寸发生了什么。

>>> signal = [[0.3394572666491664, 0.3089068053925853, 0.3516359279582483], [0.33932706934615525, 0.3094755563319447, 0.3511973743219001], [0.3394407172182317, 0.30889042266755573, 0.35166886011421256], [0.3394407172182317, 0.30889042266755573, 0.35166886011421256]]
>>> signal.shape
(4, 3)

接下来,您要计算softmax函数的雅可比矩阵。根据引用的页面,对于非对角线条目(-hi * hj的矩阵的大部分),它被定义为n_features > 2,所以让我们从那里开始。在numpy中,您可以使用broadcasting

有效地计算雅可比矩阵
>>> J = - signal[..., None] * signal[:, None, :]
>>> J.shape
(4, 3, 3)

第一个signal[..., None](相当于signal[:, :, None])将信号重新整形为(4, 3, 1),而第二个signal[:, None, :]将信号重新整形为(4, 1, 3)。然后,*只是将两个矩阵相乘。 Numpy的内部广播重复两个矩阵,以形成每个样本的n_features x n_features矩阵。

然后,我们需要修正对角元素:

>>> iy, ix = np.diag_indices_from(J[0])
>>> J[:, iy, ix] = signal * (1. - signal)

上述行提取n_features x n_features矩阵的对角线索引。这相当于做iy = np.arange(n_features); ix = np.arange(n_features)。然后,用您的defitinion hi * (1 - hi)替换对角线条目。

最后,根据链接的源,您需要对每个样本的行进行求和。这可以做到:

>>> J = J.sum(axis=1)
>>> J.shape
(4, 3)

查看摘要版本:

if derivative:
    J = - signal[..., None] * signal[:, None, :] # off-diagonal Jacobian
    iy, ix = np.diag_indices_from(J[0])
    J[:, iy, ix] = signal * (1. - signal) # diagonal
    return J.sum(axis=1) # sum across-rows for each sample

衍生物的比较:

>>> signal = [[0.3394572666491664, 0.3089068053925853, 0.3516359279582483], [0.33932706934615525, 0.3094755563319447, 0.3511973743219001], [0.3394407172182317, 0.30889042266755573, 0.35166886011421256], [0.3394407172182317, 0.30889042266755573, 0.35166886011421256]]
>>> e_x = np.exp( signal )
>>> signal = e_x / np.sum( e_x, axis = 1, keepdims = True )

此致:

>>> np.multiply( signal, 1 - signal ) + sum(
        # handle the off-diagonal values
        - signal * np.roll( signal, i, axis = 1 )
        for i in xrange(1, signal.shape[1] )
    )
array([[  2.77555756e-17,  -2.77555756e-17,   0.00000000e+00],
       [ -2.77555756e-17,  -2.77555756e-17,  -2.77555756e-17],
       [  2.77555756e-17,   0.00000000e+00,   2.77555756e-17],
       [  2.77555756e-17,   0.00000000e+00,   2.77555756e-17]])

矿:

>>> J = signal[..., None] * signal[:, None, :]
>>> iy, ix = np.diag_indices_from(J[0])
>>> J[:, iy, ix] = signal * (1. - signal)
>>> J.sum(axis=1)
array([[  4.16333634e-17,  -1.38777878e-17,   0.00000000e+00],
       [ -2.77555756e-17,  -2.77555756e-17,  -2.77555756e-17],
       [  2.77555756e-17,   1.38777878e-17,   2.77555756e-17],
       [  2.77555756e-17,   1.38777878e-17,   2.77555756e-17]])