过度拟合时,我的 LSTM 无法将错误降低到零

数据挖掘 反向传播 lstm 过拟合 成本函数
2022-02-14 15:49:50

我已经在 C++ 中实现了 LSTM,它的错误稳步减少,但在某个错误值处减慢。它似乎也可以预测大多数字符,但即使经过 5000 次反向传播迭代,它也会卡住并且无法纠正一些错误(或者会非常缓慢地纠正它们)。例如,要求它一个一个地预测字符可能会导致

abcdefg jff klmnopqrstu x wxyz

或类似的东西。请注意,网络几乎把事情做好了。此外,它在犯错后永远不会“迷失”,例如在上面的例子中,在jff之后它产生k并回到正轨,就好像它从未犯过那个错误一样。结果总是不同的——有时网络会学习所有的字母。但是,误差仍保持在相同的值。

错误从 7.0 左右开始下降到 2.35,之后它随着每次迭代而减慢,这似乎达到了一个平台期。

如果我的字母表仅由a,b组成,那么网络几乎立即意识到它应该生成abababababab,但是错误从 0.8 开始,现在总是在 0.2 到 0.28 左右

如果使用a,b,我们设置 4 个时间步长,网络会学习生成abab,但在 50 000 个反向道具之后(即使在 25 000 次之后仍然被卡住)它仅以 85% 的确定性预测“a”,即使我预计为 99.999%;必须预测“b”时的类似值。一旦卡住,它就会保持与这些类似的值。因此,在使用a,b数据集时,它可能会一遍又一遍地猜测相同的值

奇怪的是,在大多数情况下使用a,b数据集时,我观察到最终学习的概率为:

  • a=[0.68, 0.31] b=[0.15, 0.85]
  • 有时,在重新初始化网络后,它会学习最终概率为 a=[0.8205, 0.1794] 和 b=[0.1795, 0.8205]

禁用动量(前一帧的 grads 乘以零)仍然对a、b产生相同的影响

网络没有爆炸,但它的梯度似乎消失了。

问题:

通常会陷入这些价值观吗?在 26 个时间步后进行反向支撑,完成 200 000 次,到那时,变化变得缓慢。误差在 2.35 左右,不值得等待另一个 0.000001 的误差变化。

尝试使用较小的学习率 (0.0004) 可使误差降至 2.28 - 这是我所拥有的最好的。此外,使用 0.2 的动量系数;它应用于前一帧的渐变。我不会在程序执行时增加动量,而是将其保持在恒定的 0.2;

newgradient = newgradientMatrix + prevFrameGradientMatrix*0.2

我没有使用任何形式的 dropout 等。我只是希望网络过度拟合,但对于 26 字符的 aphabet,它会卡在 2.35。

当整个字母表由单个字符组成时,我只会收到 0 错误。在这种情况下,NN 将预测 aaaaaaaaaaaaaaaaa,误差将为 0

转发道具:

一切都在 CPU 中的单个线程上完成。对向量的分量使用“float”。

  • dataGate 上的 Tanh 和构建“单元”之后(在“单元”乘以输出门之前)
  • 输入、遗忘和输出门上的 Sigmoid

  • 每个门都有权重矩阵,其中每一列是从当前 LSTM 单元中的神经元到前一个 LSTM 单元中所有神经元的权重。最后一列被忽略,因为没有任何东西会影响我们的偏见。此外,偏置值(但不是它的权重!)手动设置为 1.0 只是为了确定。

  • 每个门都有一个单独的 NxN 矩阵,其循环权重(U 矩阵)在 [time-1] 时对当前 LSTM 单元的结果进行操作

  • W 和 U 都保留最后一行,因此它们采样了 lower-LSTM 的偏差。这不应该产生问题,因为这两种偏见都得到了正确的反向传播。事实上,最后一行已从 U-Matrix 中完全删除 - 只是为了确定,但无论如何,误差仍会保持在相同的 2.35 数量上。

权重初始化: 具有均匀分布的Xavier-Benjio第 253 页,右下角。统一分布的边界定义如here所述,如下所示:

low = -4*np.sqrt(6.0/(fan_in + fan_out)); // use |4| for sigmoid gates, |1| for tanh gates 
high = 4*np.sqrt(6.0/(fan_in + fan_out));

成本函数

LSTM 单元的结果是 softmaxed(忽略偏差),然后通过交叉熵函数得到单个浮点值。

交叉熵是正确的,用于多类分类。

float cost = 0;
for(each vector component){
    if(predictedVal == 0){  continue;  }
    cost += -(targetVec[i])*naturalLog(predictedVec[i]);
}
return cost;

然后将所有时间步的成本相加,并在我们进行反向传播之前返回其平均值。这是我在 2.3 处获得 26 个字符字母表的平台

顺便说一句,单元格(又名 c)和结果(又名 h)在最后一个(第 26 个时间步)之后被缓存。在反向传播之后,它们被 timestep0 使用。这可能(并且在一段时间内)被禁用,但结果是相似的。

反向传播

将列出我处理的几个重要问题和关键点:

  1. de_dh就是 (targetVec - predictVec),按这个顺序。这是因为收集的梯度将从W 和 U 矩阵中减去。这是因为在 forward prop 期间同时使用 softmax 和 crossEntropy 时,导数很好地抵消了。

  2. de_dh从 t+1添加一个额外的梯度。添加的数量是所有 4 个门的总和。为了更好地解释,回想一下这样的门之一是向前推动的,如下所示:

    dataGate = tanh(W * incomingVal + U * lstmResultPrevT + bias_ComesFrom_W_and_U); //四个门之一

    在上面的公式中,粗体表示从 [t+1] 处的这四个门之一获取梯度的数量。然后将这样的梯度在这 4 个门上相加,并添加到de_dh中,如最初所述。必须这样做,因为在前向支撑期间,[t] 的 'H' 影响了 [t+1] 的所有 4 个门

  3. 在计算 [t] 处的 Cell 的梯度时,会添加来自 [t+1] 的单元的梯度。之后,我们计算 [t-1] 的 C 的梯度,以便在我们到达较早的时间步时能够重复此过程。

  4. 在 [t-1] 处导致偏差的 U 权重的梯度是在记住偏差的原始值为 1.0 的情况下计算的;此外,它经过双重检查,以确保梯度不会从我们在 [t] 处的偏差流向 [t-1] 处的神经元。那是因为最初没有任何东西会影响我们的偏见。如下,整个U-梯度矩阵的最后一列总是0.0;

    对于 W 矩阵的这种偏置列也进行了类似的操作——整列为零。

  5. 最后,为四个门中的每一个计算 [t-1] 的每个 H 的梯度。这样做是为了使 '2. key-point' 是可能的(将 4-grads 添加到de_dh),当我们到达这个 back-prop 中的较早时间步时。

单元测试和调试:

在 20 000 次反向传播(每 26 个时间步完成一次)之后,将收集一个文件。观察到所有 4 个门的梯度都非常小,尤其是在通过每个门的激活函数之后。这是引入 Xavier init(上图)的原因之一,以防止权重太大(通过激活推回后缩小 grad)或太小(通过权重推回后缩小 grad)。

使用“规范裁剪”后观察到了显着的改进,即使使用了 56 个唯一字符并且在 56 个时间步后完成反向传播,我的 LSTM 似乎也能学习到正确的序列。与原始示例(使用 26 个字符)类似,只有几个字符被错误预测。然而,误差始终处于稳定状态,值较高(约 4.5)

再一次,这是传统的行为,我只需要依靠 dropout 和平均多个网络的结果吗?然而,似乎我的网络甚至无法过度拟合......


编辑:

我发现了一件事——LSTM的结果是向量,它的分量不能小于-1或大于1(tanh和sigmoid的curtesy)因此,不能小于~0.36或大于~ 2.71ex

所以概率总是有一些“沉淀”悬空,网络总是“担心”不能达到100%的置信度?试图在这里澄清

1个回答

正如我的问题的最后一次编辑中所述,问题确实与 softmax 函数有关。

正如这里澄清的那样,我们不应该将 softmax 直接应用于最后一个 LSTM 的结果。请注意,LSTM 将生成一个值向量,每个值都在 -1 和 1 之间(由于应用于 Cell 的 tanh 压缩函数)。

相反,我创建了一个传统的全连接层(只是额外的权重矩阵),并将 LSTM 的结果提供给该层。这个“输出”层没有被激活——它输入到一个 softmax 函数中,它实际上作为一个激活函数。

我修改了反向传播算法,将 softmax 生成的梯度提供给输出层。当然,如果您最初使用了交叉熵成本函数,那么这样的梯度将保留然后将其推过该输出层的权重,以获得 LSTM 的梯度。在此之后,反向传播照常应用,网络最终收敛。(predictedexpected)

编辑:势头略有改善。

此外,使用 0.2 的动量系数;它应用于前一帧的渐变。我不会在程序执行时增加动量,而是将其保持在恒定的 0.2;

newgradient = newgradientMatrix + prevFrameGradientMatrix*0.2

这很好,但是改变动量系数需要我们也重新调整学习率。更简洁的版本将是:

newgradient = newgradientMatrix*(1-0.9) + prevFrameGradientMatrix*0.9

这是一个指数移动平均线,大约记得天。10.1=10

第 10 天,系数为因为较新的 9 天具有较大的系数(大于 0.387),所以它们的平均值确实使第 10 天及以上可以忽略不计

(1ϵ)1ϵ0.9=(10.1)10.10.9=0.9100.90.3871e

任何更早的日子都会有更少的贡献

此外,不要忘记偏差校正,这有助于在我们“刚刚开始”计算平均值时获得更好的估计。如果没有偏差修正,它将开始非常低,并且需要一些时间才能赶上预期的“指数移动平均线”

newval = curval*(1-0.9) + prevVal*0.9
newval /= 1-(0.9)^t  //where t is timestep

然而,在实践中,大约 10 个时间步之后就可以了 - 因此在 Momentum 中没有真正需要偏差校正,但如果我们使用 Adam 的组合(Momentum 和 RMSProp 的组合)就应该这样做