Python 神经网络中的梯度计算问题(MNIST中的错误率为7%)

Python 神经网络中的梯度计算问题(MNIST中的错误率为7%),python,numpy,neural-network,Python,Numpy,Neural Network,您好,我在使用numpy在python中实现神经网络时,在计算检查梯度时遇到了一个问题。 我正在使用mnistdataset尝试并尝试使用小批量梯度下降 我已经检查了数学,纸上看起来不错,所以也许你可以给我一个关于这里发生了什么的提示: 编辑:一个答案让我意识到成本函数的计算确实是错误的。然而,这并不能解释梯度的问题,因为它是使用back_prop计算的。我在隐藏层中使用300个单位,使用minibatch gradient下降和rmsprop,30个历元和100个批次,获得了%7的错误率。(l

您好,我在使用numpy在python中实现神经网络时,在计算检查梯度时遇到了一个问题。 我正在使用
mnist
dataset尝试并尝试使用小批量梯度下降

我已经检查了数学,纸上看起来不错,所以也许你可以给我一个关于这里发生了什么的提示:

编辑:一个答案让我意识到成本函数的计算确实是错误的。然而,这并不能解释梯度的问题,因为它是使用back_prop计算的。我在隐藏层中使用300个单位,使用
minibatch gradient
下降和
rmsprop
,30个历元和100个批次,获得了%7的错误率。(
learning_rate
=0.001,由于rmsprop,因此较小)

每个输入都有768个特征,因此对于100个样本,我有一个矩阵
Mnist
有10个类

X=NoSamplesxFeatures=100x768

Y=NoSamplesxClasses=100x10

我使用一个隐层神经网络,当完全训练时,隐层大小为300。另一个问题是我是否应该使用softmax输出函数来计算误差。。。我想不是。但我对这一切都是新手,显而易见的事情对我来说可能很奇怪

(注意:我知道代码很难看,但这是我在压力下完成的第一个Python/Numpy代码,请耐心等待)

下面是返回和激活:

  def sigmoid(z):
     return np.true_divide(1,1 + np.exp(-z) )

  #not calculated really - this the fake version to make it faster. 
  def sigmoid_prime(a):
     return  (a)*(1 - a)

  def _back_prop(self,W,X,labels,f=sigmoid,fprime=sigmoid_prime,lam=0.001):

    """
    Calculate the partial derivates of the cost function using backpropagation.
    """     
    #Weight for first layer and hidden layer
    Wl1,bl1,Wl2,bl2  = self._extract_weights(W)


    # get the forward prop value
    layers_outputs = self._forward_prop(W,X,f)

    #from a number make a binary vector, for mnist 1x10 with all 0 but the number.
    y = self.make_1_of_c_encoding(labels)
    num_samples = X.shape[0] # layers_outputs[-1].shape[0]

    # Dot product return  Numsamples (N) x Outputs (No CLasses)
    # Y is NxNo Clases
    # Layers output to


    big_delta = np.zeros(Wl2.size + bl2.size + Wl1.size + bl1.size)
    big_delta_wl1, big_delta_bl1, big_delta_wl2, big_delta_bl2 = self._extract_weights(big_delta)


    # calculate the gradient for each training sample in the batch and accumulate it

    for i,x in enumerate(X):

        # Error with respect  the output
        dE_dy =  layers_outputs[-1][i,:] -  y[i,:] 

        # bias hidden layer
        big_delta_bl2 +=   dE_dy


        # get the error for the hiddlen layer
        dE_dz_out  = dE_dy * fprime(layers_outputs[-1][i,:])

        #and for the input layer
        dE_dhl = dE_dy.dot(Wl2.T)

        #bias input layer
        big_delta_bl1 += dE_dhl

        small_delta_hl = dE_dhl*fprime(layers_outputs[-2][i,:])

        #here calculate the gradient for the weights in the hidden and first layer
        big_delta_wl2 += np.outer(layers_outputs[-2][i,:],dE_dz_out)
        big_delta_wl1 +=   np.outer(x,small_delta_hl)





    # divide by number of samples in the batch (should be done here)?
    big_delta_wl2 = np.true_divide(big_delta_wl2,num_samples) + lam*Wl2*2
    big_delta_bl2 = np.true_divide(big_delta_bl2,num_samples)
    big_delta_wl1 = np.true_divide(big_delta_wl1,num_samples) + lam*Wl1*2
    big_delta_bl1 = np.true_divide(big_delta_bl1,num_samples)

    # return 
    return np.concatenate([big_delta_wl1.ravel(),
                           big_delta_bl1,
                           big_delta_wl2.ravel(),
                           big_delta_bl2.reshape(big_delta_bl2.size)])
现在是前馈:

def _forward_prop(self,W,X,transfer_func=sigmoid):
    """
    Return the output of the net a Numsamples (N) x Outputs (No CLasses)
    # an array containing the size of the output of all of the laye of the neural net
    """

    # Hidden layer DxHLS
    weights_L1,bias_L1,weights_L2,bias_L2 = self._extract_weights(W)    

    # Output layer HLSxOUT

    # A_2 = N x HLS
    A_2 = transfer_func(np.dot(X,weights_L1) + bias_L1 )

    # A_3 = N x  Outputs
    A_3 = transfer_func(np.dot(A_2,weights_L2) + bias_L2)

    # output layer
    return [A_2,A_3]
以及梯度检查的成本函数:

 def cost_function(self,W,X,labels,reg=0.001):
    """
    reg: regularization term
    No weight decay term - lets leave it for later
    """

    outputs = self._forward_prop(W,X,sigmoid)[-1] #take the last layer out
    sample_size = X.shape[0]

    y = self.make_1_of_c_encoding(labels)

    e1 = np.sum((outputs - y)**2, axis=1))*0.5

    #error = e1.sum(axis=1)
    error = e1.sum()/sample_size + 0.5*reg*(np.square(W)).sum()

    return error

当你运行梯度检查时,你会得到什么样的结果?通常情况下,通过查看渐变的输出与渐变检查产生的输出,您可以梳理出实现错误的性质

此外,对于分类任务(如MNIST),平方误差通常是一个糟糕的选择,我建议使用简单的sigmoid顶层或softmax。对于sigmoid,要使用的交叉熵函数是:

L(h,Y) = -Y*log(h) - (1-Y)*log(1-h)
对于softmax

L(h,Y) = -sum(Y*log(h))
其中Y是作为1x10向量给出的目标,h是您的预测值,但很容易扩展到任意批量大小

在这两种情况下,顶层三角洲简单地变成:

delta = h - Y
顶层梯度变为:

grad = dot(delta, A_in)
其中,A_in是上一层到顶层的输入

虽然我很难理解你的backprop例程,但我从你的代码中怀疑渐变中的错误是由于在使用平方误差时没有正确计算顶级dE/dw_l2,以及在不正确的输入上计算fprime

使用平方误差时,顶层增量应为:

delta = (h - Y) * fprime(Z_l2)
这里Z_l2是第2层传递函数的输入。同样,在计算较低层的fprime时,您希望使用传递函数的输入(即点(X,权重_L1)+偏差_L1)

希望有帮助

编辑: 作为使用交叉熵误差优于平方误差的额外理由,我建议查阅Geoffrey Hinton关于线性分类方法的讲座:

编辑2: 我在MNIST数据集上用我的神经网络实现在本地运行了一些测试,这些数据集具有不同的参数,并且使用RMSPROP实现了一个隐藏层。结果如下:

Test1
Epochs: 30
Hidden Size: 300 
Learn Rate: 0.001
Lambda: 0.001
Train Method: RMSPROP with decrements=0.5 and increments=1.3 
Train Error: 6.1%
Test Error: 6.9%

Test2
Epochs: 30
Hidden Size: 300 
Learn Rate: 0.001
Lambda: 0.000002
Train Method: RMSPROP with decrements=0.5 and increments=1.3 
Train Error: 4.5%
Test Error: 5.7%

如果将lambda参数减小两个数量级,您应该会得到更好的性能。

运行渐变检查时会得到什么样的结果?通常情况下,通过查看渐变的输出与渐变检查产生的输出,您可以梳理出实现错误的性质

此外,对于分类任务(如MNIST),平方误差通常是一个糟糕的选择,我建议使用简单的sigmoid顶层或softmax。对于sigmoid,要使用的交叉熵函数是:

L(h,Y) = -Y*log(h) - (1-Y)*log(1-h)
对于softmax

L(h,Y) = -sum(Y*log(h))
其中Y是作为1x10向量给出的目标,h是您的预测值,但很容易扩展到任意批量大小

在这两种情况下,顶层三角洲简单地变成:

delta = h - Y
顶层梯度变为:

grad = dot(delta, A_in)
其中,A_in是上一层到顶层的输入

虽然我很难理解你的backprop例程,但我从你的代码中怀疑渐变中的错误是由于在使用平方误差时没有正确计算顶级dE/dw_l2,以及在不正确的输入上计算fprime

使用平方误差时,顶层增量应为:

delta = (h - Y) * fprime(Z_l2)
这里Z_l2是第2层传递函数的输入。同样,在计算较低层的fprime时,您希望使用传递函数的输入(即点(X,权重_L1)+偏差_L1)

希望有帮助

编辑: 作为使用交叉熵误差优于平方误差的额外理由,我建议查阅Geoffrey Hinton关于线性分类方法的讲座:

编辑2: 我在MNIST数据集上用我的神经网络实现在本地运行了一些测试,这些数据集具有不同的参数,并且使用RMSPROP实现了一个隐藏层。结果如下:

Test1
Epochs: 30
Hidden Size: 300 
Learn Rate: 0.001
Lambda: 0.001
Train Method: RMSPROP with decrements=0.5 and increments=1.3 
Train Error: 6.1%
Test Error: 6.9%

Test2
Epochs: 30
Hidden Size: 300 
Learn Rate: 0.001
Lambda: 0.000002
Train Method: RMSPROP with decrements=0.5 and increments=1.3 
Train Error: 4.5%
Test Error: 5.7%

如果将lambda参数减少几个数量级,您的性能应该会更好。

您好,M.H.谢谢您的回答。你的评论使我修改了概念。所以有两件事需要澄清:1。我使用sigmoid输出函数。Ad fprime定义为=(a)*(1-a),用于更快的计算,其中a是输出(以前由前馈计算)。这就是为什么它看起来有点奇怪。根据你所说的,成本函数的定义是错误的,但是梯度应该给出一个很好的答案。2.我当时使用了错误的成本函数,但这可以解释梯度计算的问题。但这不是我在300个隐藏单位和30个纪元的情况下只得到7%错误的原因。谢谢你的澄清