4、输入门详解:Sigmoid与Tanh层如何协同工作,决定存储哪些新信息。

好,咱们接着聊LSTM的输入门。

上一节我们讲了遗忘门,它负责决定「扔掉什么」。那输入门呢?正好相反——它决定「记住什么新东西」。

说白了,输入门就是LSTM的「筛选器」。新来的信息那么多,不能全盘接收吧?得挑一挑,拣有用的存下来。

4.1 输入门的两个核心部件

输入门其实不是「一个」门,而是两个层在协同工作。我刚开始学的时候也困惑过,以为就一个Sigmoid完事。后来才发现,原来是「双人舞」。

  • Sigmoid层(输入门):决定哪些值需要更新。输出0到1之间,0表示「不更新」,1表示「全更新」。
  • Tanh层(候选记忆):生成新的候选值向量。输出-1到1之间,表示「新信息的内容」。

这两个层的结果会逐元素相乘,得到最终要存入细胞状态的新信息。

你想想看,Sigmoid像不像一个「开关」?它控制着哪些位置要写入。Tanh则像「笔」,负责写出具体内容。开关和笔配合好了,才能正确写入。

4.2 数学公式与直观理解

公式其实不复杂,我写给你看:

i_t = σ(W_i · [h_{t-1}, x_t] + b_i)    // 输入门,决定更新哪些
Ĉ_t = tanh(W_c · [h_{t-1}, x_t] + b_c) // 候选记忆,生成新内容

然后,新的细胞状态更新为:

C_t = f_t * C_{t-1} + i_t * Ĉ_t

注意看这个公式。遗忘门 f_t 乘以旧状态 C_{t-1},这是「丢弃旧信息」。输入门 i_t 乘以候选记忆 Ĉ_t,这是「添加新信息」。

嗯,这里要注意:i_tĈ_t 的维度必须一致,这样才能逐元素相乘。我在项目中遇到过维度不匹配的bug,排查了半天才发现是拼接时顺序搞反了。

4.3 为什么需要两个激活函数?

你可能想问:为什么不用一个函数搞定?

我个人习惯这样理解:

  • Sigmoid 输出范围0~1,天然适合做「门控」。它告诉模型「这个位置的信息重要程度是多少」。
  • Tanh 输出范围-1~1,适合做「内容表示」。它允许信息有正有负,表达能力更强。

如果只用Sigmoid,那新信息只能是0到1之间的正数,没法表达「负向信息」。如果只用Tanh,那所有信息都会无差别地写入,没有筛选机制。

所以,两者缺一不可。一个负责「要不要写」,一个负责「写什么」。

核心要点:输入门的本质是「选择性写入」。Sigmoid提供选择,Tanh提供内容。两者相乘,就是「有选择地写入新内容」。

4.3 输入门的工作流程(SVG图解)

下面这张图展示了输入门的完整流程。我建议你仔细看看,尤其是 i_tĈ_t 如何汇合。

输入门工作流程 h_{t-1} x_t 拼接 Sigmoid (σ) Tanh i_t (0~1) Ĉ_t (-1~1) × i_t * Ĉ_t Sigmoid:决定更新哪些位置(开关) Tanh:生成候选新信息(内容) 逐元素相乘

小技巧:在实际调试LSTM时,可以单独打印 i_tĈ_t 的值。如果 i_t 大部分接近0,说明模型几乎不写入新信息,可能是梯度消失或学习率太低。如果 i_t 大部分接近1,说明模型在疯狂写入,容易过拟合。

4.4 输入门与遗忘门的配合

输入门不是孤立工作的。它和遗忘门是一对「搭档」。

遗忘门决定丢弃多少旧信息,输入门决定添加多少新信息。两者共同维护细胞状态 C_t 的更新。

我曾经在一个时间序列预测项目中,发现模型总是学不到长期依赖。排查后发现,遗忘门几乎全开(接近1),输入门也全开(接近1),导致细胞状态被新信息不断冲刷,旧信息根本留不住。

后来我调整了偏置初始化,让遗忘门的偏置初始化为1.0(偏向记住),输入门的偏置初始化为0.0(偏向不写)。效果立竿见影。

注意:输入门的输出 i_t 如果长期接近0.5,说明模型对「是否写入」犹豫不决。这时候可以检查一下输入特征是否噪声太大,或者试试调整学习率。

4.5 代码示例:手动实现输入门

下面我用PyTorch写一个简单的输入门实现,方便你理解内部机制。

import torch
import torch.nn as nn

class InputGate(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(InputGate, self).__init__()
        # Sigmoid部分:决定更新哪些
        self.W_i = nn.Linear(input_size + hidden_size, hidden_size)
        # Tanh部分:生成候选记忆
        self.W_c = nn.Linear(input_size + hidden_size, hidden_size)
        
    def forward(self, x, h_prev):
        # 拼接输入和上一时刻隐藏状态
        combined = torch.cat([h_prev, x], dim=-1)
        
        # 输入门:Sigmoid
        i_t = torch.sigmoid(self.W_i(combined))
        
        # 候选记忆:Tanh
        c_tilde = torch.tanh(self.W_c(combined))
        
        return i_t, c_tilde

# 使用示例
input_size = 10
hidden_size = 20
input_gate = InputGate(input_size, hidden_size)

x = torch.randn(1, input_size)      # 当前输入
h_prev = torch.randn(1, hidden_size) # 上一时刻隐藏状态

i_t, c_tilde = input_gate(x, h_prev)
print(f"输入门 i_t 的形状: {i_t.shape}")      # [1, 20]
print(f"候选记忆 Ĉ_t 的形状: {c_tilde.shape}") # [1, 20]
print(f"i_t 的值范围: [{i_t.min().item():.3f}, {i_t.max().item():.3f}]")
print(f"Ĉ_t 的值范围: [{c_tilde.min().item():.3f}, {c_tilde.max().item():.3f}]")

运行这段代码,你会看到 i_t 的值都在0~1之间,而 Ĉ_t 的值在-1~1之间。这就是两个激活函数的区别。

4.6 输入门的常见误区

误区 正确理解
输入门 = Sigmoid层 输入门是Sigmoid + Tanh的组合,两者缺一不可
Tanh输出就是新细胞状态 Tanh输出只是候选值,需要乘以Sigmoid门控后才写入
输入门和遗忘门独立工作 两者共同决定细胞状态的更新,是协同关系
输入门值越大越好 输入门值过大可能导致旧信息被快速覆盖,丢失长期依赖

一句话总结:输入门 = Sigmoid(开关) × Tanh(内容)。开关决定写不写,内容决定写什么。两者相乘,就是「有选择地写入新信息」。

好了,输入门就讲到这里。下一节我们聊聊输出门,看看LSTM如何从细胞状态中提取有用信息。


专注资料整理