Transformer(3): Add Norm
文章目录
- 残差连接
- 层归一化
- 作用
- 和其它归一化的比较
- dropout
- 基本思想
- 实现方式
残差连接
目的是解决深层网络中梯度消失的问题。
解决方法是进行跳跃连接,即多出一条输出链路,将输入X直接接到输出层上。
公式为 F(X) + X。
这样每一层求导的时候至少能够保证是>1的,不会产生梯度消失问题。
层归一化
作用
对输入的每个样本进行归一化处理,即每个特征维度的均值和方差会被调整为0和1,帮助加速训练并稳定模型。
和其它归一化的比较
NLP 任务通常处理变长的序列(如句子),层归一化在处理这些变长序列时非常合适,因为它不依赖于批量数据,而是每个样本独立进行归一化。
BN的问题
- 批次依赖性:BN 需要一个批次的数据来计算均值和方差。在NLP中,尤其是当输入数据长度不一致(例如,句子长度不同)时,批次大小可能变化。这使得在处理变长的序列或小批量数据时,BN 的效果不好,且计算上的不稳定性可能导致性能下降。
- 推理时问题:在推理阶段(即在测试或推理过程中),BN 需要依赖全批次的统计数据(均值和方差),这使得在处理单个样本时变得困难,尤其是在 NLP 中通常会处理单句或单文本(例如生成任务中一次生成一个单词)。
dropout
基本思想
在训练的时候以P的概率丢弃神经元,所以这样每一次的传播都会走的不一样的路径,具有集成学习的特点,并且也避免了过度依赖于关键神经元。
实现方式
有一个Mask向量,是一个随机的二进制向量,遵从于Bernoulli(1−p),即01分布,0表示丢弃,1表示保留,p概率的丢弃,所以保留的概率是1-p。
训练的时候每个神经元的输出都被压缩了,即输出变成不加dropout的1 / (1 - p),在测试的时候就所有神经元都保持激活,用于所有学习到的特征进行推理,但是我们需要放大输出,即把训练缩小的补偿回来,就是输出除于 (1 - p).
class AddNorm(nn.Module):
def __init__(self, *args, **kwargs) -> None:
super(AddNorm, self).__init__(*args, **kwargs)
self.add_norm = nn.LayerNorm(num_hiddens)
self.dropout = nn.Dropout(0.1)
def forward(self, X, X1):
X1 = self.add_norm(X1)
X = X + X1
X = self.dropout(X)
return X