当前位置: 首页 > 知识库问答 >
问题:

训练神经网络出现极小值或NaN值

宋涵衍
2023-03-14

我正在尝试在Haskell中实现一个神经网络架构,并在MNIST上使用它。

我使用hmatrix包来处理线性代数。我的培训框架是使用pipes包构建的。

我的代码会编译,不会崩溃。但问题是,层大小(例如1000)、小批大小和学习速率的某些组合会在计算中产生nan值。经过一些检查,我看到极小的值(1E-100)最终出现在激活中。但是,即使这种情况没有发生,训练仍然不起作用。它的损耗和精确度都没有改善。

我检查了一遍又一遍我的代码,我不知道问题的根源是什么。

下面是反向传播训练,它计算每一层的增量:

backward lf n (out,tar) das = do
    let δout = tr (derivate lf (tar, out)) -- dE/dy
        deltas = scanr (\(l, a') δ ->
                         let w = weights l
                         in (tr a') * (w <> δ)) δout (zip (tail $ toList n) das)
    return (deltas)

LF是损失函数N是网络(weight矩阵和bias每层的矢量),outtarget是网络的实际输出和target(期望的)输出,das是每层的激活导数。

在批处理模式下,outtar是矩阵(行是输出向量),das是矩阵列表。

下面是实际的梯度计算:

  grad lf (n, (i,t)) = do
    -- Forward propagation: compute layers outputs and activation derivatives
    let (as, as') = unzip $ runLayers n i
        (out) = last as
    (ds) <- backward lf n (out, t) (init as') -- Compute deltas with backpropagation
    let r  = fromIntegral $ rows i -- Size of minibatch
    let gs = zipWith (\δ a -> tr (δ <> a)) ds (i:init as) -- Gradients for weights
    return $ GradBatch ((recip r .*) <$> gs, (recip r .*) <$> squeeze <$> ds)

这里,lfn与上面相同,i是输入,t是目标输出(都是批处理形式,作为矩阵)。

squeze通过对每一行求和将矩阵转换为向量。也就是说,ds是一个增量矩阵列表,其中每列对应于小型批处理的一行的增量。因此,偏差的梯度是所有小批量上差值的平均值。对于gs也是如此,它对应于权重的梯度。

以下是实际的更新代码:

move lr (n, (i,t)) (GradBatch (gs, ds)) = do
    -- Update function
    let update = (\(FC w b af) g δ -> FC (w + (lr).*g) (b + (lr).*δ) af)
        n' = Network.fromList $ zipWith3 update (Network.toList n) gs ds
    return (n', (i,t))

lr是学习率。fc是层构造函数,af是该层的激活函数。

梯度下降算法确保传递一个负值的学习率。梯度下降的实际代码只是围绕gradmove组成的循环,带有参数化的停止条件。

最后,这里是均方误差损失函数的代码:

mse :: (Floating a) => LossFunction a a
mse = let f (y,y') = let gamma = y'-y in gamma**2 / 2
          f' (y,y') = (y'-y)
      in  Evaluator f f'

evaluator只是捆绑了一个损失函数及其导数(用于计算输出层的delta)。

其余的代码在GitHub上:NeuralNetwork。

所以,如果有人能洞察到这个问题,甚至只是检查一下我是否正确地实现了这个算法,我将不胜感激。

共有1个答案

孙项禹
2023-03-14

你知道反向传播中的“消失”和“爆炸”梯度吗?我对Haskell不太熟悉,所以我不能很容易地看到你的后盾到底在做什么,但它看起来确实像是你在使用逻辑曲线作为你的激活函数。

如果你看一下这个函数的曲线图,你会看到这个函数的梯度在末端几乎为0(当输入值变得很大或很小时,曲线的斜率几乎是平坦的),因此在反向传播过程中乘以或除以这个函数将得到一个很大或很小的数字。当你穿过多个层时,重复地这样做会导致激活接近于零或无穷大。由于backprop在训练过程中通过这样做来更新你的权重,所以你的网络中会出现大量的0或无穷大。

解决方法:有很多方法可以用来解决渐变消失问题,但是一个很容易尝试的事情是改变你使用的激活函数的类型,变成一个不饱和的。ReLU是一个流行的选择,因为它减轻了这个特殊的问题(但可能会引入其他问题)。

 类似资料:
  • 我有一个关于卷积神经网络()训练的问题。 我成功地使用tensorflow训练了一个网络,它获取一个输入图像(1600像素),然后输出三个匹配的类中的一个。 使用不同的培训课程测试网络,效果良好。然而当我给它一个不同的第四个图像(不包含任何经过训练的3个图像)时,它总是返回一个随机匹配到其中一个类。 我的问题是,如何训练网络来分类图像不属于这三个训练图像中的任何一个?类似的例子是,如果我针对mni

  • 在本章中,我们将了解可以使用TensorFlow框架实现的神经网络训练的各个方面。 以下几个建议,可以评估 - 1. 反向传播 反向传播是计算偏导数的简单方法,其中包括最适合神经网络的基本形式的合成。 2. 随机梯度下降 在随机梯度下降中,批处理是示例的总数,用户用于在单次迭代中计算梯度。到目前为止,假设批处理已经是整个数据集。最好的例子是谷歌规模; 数据集通常包含数十亿甚至数千亿个示例。 3.

  • 第 10 章介绍了人工神经网络,并训练了第一个深度神经网络。 但它非常浅,只有两个隐藏层。 如果你需要解决非常复杂的问题,例如检测高分辨率图像中的数百种类型的对象,该怎么办? 你可能需要训练更深的 DNN,也许有 10 层或更多,每层包含数百个神经元,通过数十万个连接相连。 这可不像公园散步那么简单,可能碰到下面这些问题: 你将面临棘手的梯度消失问题(或相关的梯度爆炸问题):在反向传播过程中,梯度

  • 第 10 章介绍了人工神经网络,并训练了我们的第一个深度神经网络。 但它是一个非常浅的 DNN,只有两个隐藏层。 如果你需要解决非常复杂的问题,例如检测高分辨率图像中的数百种类型的对象,该怎么办? 你可能需要训练更深的 DNN,也许有 10 层,每层包含数百个神经元,通过数十万个连接来连接。 这不会是闲庭信步: 首先,你将面临棘手的梯度消失问题(或相关的梯度爆炸问题),这会影响深度神经网络,并使较

  • 我们现在将学习如何训练神经网络。 我们还将学习Python深度学习中的反向传播算法和反向传递。 我们必须找到神经网络权重的最佳值,以获得所需的输出。 为了训练神经网络,我们使用迭代梯度下降法。 我们最初从权重的随机初始化开始。 在随机初始化之后,我们使用前向传播过程对数据的某个子集进行预测,计算相应的成本函数C,并将每个权重w更新为与dC/dw成比例的量,即成本函数的导数。重量。 比例常数称为学习

  • 我知道前馈神经网络的基本知识,以及如何使用反向传播算法对其进行训练,但我正在寻找一种算法,以便使用强化学习在线训练神经网络。 例如,我想用人工神经网络解决手推车杆摆动问题。在这种情况下,我不知道应该怎么控制钟摆,我只知道我离理想位置有多近。我需要让安在奖惩的基础上学习。因此,监督学习不是一种选择。 另一种情况类似于蛇游戏,反馈被延迟,并且仅限于进球和反进球,而不是奖励。 我可以为第一种情况想出一些