头部层:跳跃连接¶
在微调模型时,如何保留原始模型的预测能力是一个特别突出的问题。最大程度地减少在目标数据集上的损失并使其在一般情况下也能正常工作至关重要。换句话话,我们需要避免灾难性遗忘。
一种常见但天真的方法是冻结基础模型,仅训练新初始化的顶层,然后可能再解冻。
然而,这并没有解决头部层本身的问题。当随机初始化时,它会破坏来自主编码器的有用信号。
这可能导致多种问题,例如在训练数据集上过拟合、陷入局部最小值以及不稳定的损失值等,从而无法有效调整基础模型的参数。
解决这些问题的一种可能方案是使用跳跃连接(Skip-Connection)架构作为最后一层。
┌───────────────┐
│ Encoder │
└───────┬───────┘
├──────────┐
┌───────┴───────┐ │
│ Skip-Dropout │ │
└───────┬───────┘ │
┌───────┴───────┐ │
│ Linear │ │
└───────┬───────┘ │
┌───────┴───────┐ │
│ Gated │ │
└───────┬───────┘ │
+ <────────┘
│
跳跃连接层类似于ResNet 架构中引入的残差块。
该层通过两条路径传递信号:一条路径保持不变,另一条路径则通过线性层和门控层。门控层充当开关的作用。如果它的某个元素被置零,该层就不会改变该元素中的信号。否则,信号将是原始信号和转换信号的组合。
由于门控层最初被置零,模型在训练开始时的输出嵌入等于预训练编码器的嵌入。这使得您可以在训练初期获得良好的梯度,并且不会丢失有用的信号。
在 Quaterion 中使用 SkipConnection 就像使用常规线性层一样简单
class Model(TrainableModel):
...
def configure_head(self, input_embedding_size: int) -> EncoderHead:
return SkipConnectionHead(input_embedding_size)