基于Transformer解决机器翻译问题的思考与总结#Datawhale AI 夏令营

纯小白,整理总结自己这几天学到的知识,以及一些体会

1 Transformer

1.1 结构

Transformer的主要结构包括:编码器输入,编码器(Encoder)嵌入表示层、注意力层、前瞻层,解码器输入,解码器(Decoder)注意力层、编码解码注意力层、前瞻层,其中将编码器的输出作为解码器输入的一部分

在整个Transformer中编码器和解码器数量N是对应相同的(如下图中的“×6”,表示各有6个,共6组),这N个编码器结构相同,N个解码器结构也相同(当然,编码器和解码器的结构不同),但是每一组编码器和解码器参数并不相同(即1~N组的编码器参数不同,1~N组的解码器参数不同给),在训练的时候每组参数也是在独立训练,并最后通过Albert进行处理,最终达到减少参数的效果(具体是什么原理我也没搞明白)

Transformer主要的结构

1.2 功能

1.2.1 嵌入表示层

在机器翻译中我们不仅仅要知道源语言单词和目标语言单词的对应关系,还要知道其对应的位置,这个位置包括词的绝对位置以及词与词之间的相对位置

比如将英语”read good book“翻译成汉语“读好书”,我们不仅要知道“read”的意思是”读“,”good”的意思是“好”,“book”的意思是“书”,还要知道每个对应词汇的位置,否则就会出现“读书好”“好读书”“书好读”等错误翻译

对于RNN来说,因为其在翻译“read good book”的时候的串联运算的,即翻译完“read’再翻译”good“”book“,其结果的呈现已经带有了位置信息,但是Transformer是在进行并行运算,这三个英语的单词是同时被翻译出来的,这虽然提升了运算效率,但是也导致位置信息缺失,所以我们需要加入嵌入表示层

嵌入表示层主要包括两部分:输入嵌入层(Input Embedding)和位置编码(Position Encoding),二者结果的和(两个向量叠加)将被用作模型的输入

1.2.1.1 输入嵌入层

输入嵌入层负责将每个单词转化为其相对应的向量表示,方法包括随机初始化、word2vec等,这个可以理解成该单词的绝对位置

1.2.1.2 位置编码

位置编码是基于每个单词对应的向量所得到另一个向量,它可以反映不同单词之间的相对位置。

位置编码公式

通过位置编码公式和每个单词对应的向量我们便可以计算出位置编码,下面是我的一些理解:

a. 关注 2i 和 2i+1 ,它表示在偶数列使用sin函数计算,奇数列使用cos函数计算,i本身的含义可能没那么重要(主要我没弄明白具体的含义,希望有佬能解惑)

b. 使用三角函数计算位置编码,因为

sin(a+b)=sina cosb+sinb cosa   cos(a+b)=cosa cosb-sina sinb

对于pos和pos+k两个相距为k的位置,这两个位置的位置向量,可以使用pos位置的位置向量和k位置的位置向量来线性表示,这也就意味着位置向量中包含了相对位置信息

c. 因为正余弦函数的值在[-1,1],所以原词对应的向量在叠加位置编码后并不会偏离太多,从而保证了语义信息不被破坏

d. 相对位置的信息会在注意力机制那里消失(没理解为什么,也不理解既然消失了为什么模型可以自动学会使用这个相对位置信息,求佬解惑)

1.2.2 注意力层

显然两个向量的点乘结果是一个向量在两一个向量上的投影长度,利用这一点,我们可以利用一个向量与多个向量点乘,通过得到的值来判断该向量与其它向量的关联程度。

在注意力层中,为了得到编码某个单词时所需要关注的上下文信息,我们使用上述的原理,将该单词位置向量(q)与其他位置的键向量(k)做点积得到匹配分数,匹配分数越高,向量越相似,注意力越集中。为了放置过大的匹配分数在后续Softmax计算过程中导致的梯度爆炸以及收敛效率差的问题,这些得分会除以放缩因子\sqrt{d}以稳定优化。这一过程可以聚合希望关注的上下文信息,并最小化不相关信息的干扰,可以被形式化地表述如下:

1.2.3 前馈层

前馈层接受自注意力子层的输出作为输入,并通过一个带有 Relu 激活函数的两层全连接网络对输入进行更加复杂的非线性变换。实验证明,这一非线性变换会对模型最终的性能产生十分重要的影响。

其中 $$W_{1},b_{1},W_{2},b_{2}$$ 表示前馈子层的参数。另外,以往的训练发现,增大前馈子层隐状态的维度有利于提升最终翻译结果的质量,因此,前馈子层隐状态的维度一般比自注意力子层要大

1.2.4 残差连接与层归一化

残差连接主要是指使用一条直连通道直接将对应子层的输入连接到输出上去,从而避免由于网络过深在优化过程中潜在的梯度消失问题,这一点可以利用向后传播的链式法则得到显然的证明

层归一化我没有整明白,这里直接放上datawhale提供的资料

为了进一步使得每一层的输入输出范围稳定在一个合理的范围内,层归一化技术被进一步引入每个 Transformer 块的当中:

其中 \mu 和 \sigma 分别表示均值和方差,用于将数据平移缩放到均值为 0,方差为 1 的标准分布,\alpha  和 \beta 是可学习的参数。层归一化技术可以有效地缓解优化过程中潜在的不稳定、收敛速度慢等问题

1.2.5 解码器

在训练模型时,我们可以让模型同时看到源语言和目标语言,但是在实际翻译过程中,模型只能看见源语言和已经从源语言翻译出来的目标语言,对于正在翻译以及未翻译信息对应的目标语言,它是不知道的,所以我们为了拟合这一情况,在解码器中增加了掩盖后续文本信息的掩码,以防模型无法得到有效训练

此外,解码器端还额外增加了一个多头注意力(Multi-Head Attention)模块,使用交叉注意力(Cross-attention)方法,同时接收来自编码器端的输出以及当前 Transformer 块的前一个掩码注意力层的输出。查询是通过解码器前一层的输出进行投影的,而键和值是使用编码器的输出进行投影的。它的作用是在翻译的过程当中,为了生成合理的目标语言序列需要观测待翻译的源语言序列是什么。基于上述的编码器和解码器结构,待翻译的源语言文本,先经过编码器端的每个Transformer 块对其上下文语义的层层抽象,然后输出每一个源语言单词上下文相关的表示。解码器端以自回归的方式生成目标语言文本,即在每个时间步 t ,根据编码器端输出的源语言文本表示,以及前 t-1 个时刻生成的目标语言文本,生成当前时刻的目标语言单词。

2 可以选择的上分策略

2.1 调整参数

最简单的就是调参,将 epochs 调大一点,使用全部训练集,以及调整模型的参数,如head、layers等。如果数据量允许,增加模型的深度(更多的编码器/解码器层)或宽度(更大的隐藏层尺寸),这通常可以提高模型的表达能力和翻译质量,尤其是在处理复杂或专业内容时。

2.2 加入术语词典

  • 模型生成的翻译输出中替换术语,这是最简单的方法

  • 整合到数据预处理流程,确保它们在翻译中保持一致

  • 在模型内部动态地调整术语的嵌入,这涉及到在模型中加入一个额外的层,该层负责查找术语词典中的术语,并为其生成专门的嵌入向量,然后将这些向量与常规的词嵌入结合使用

2.3 数据清洗

拟声词或者源语言没有但是目标语言中有的注释

2.4 数据扩增

  • 回译(back-translation):将源语言文本先翻译成目标语言,再将目标语言文本翻译回源语言,生成的新文本作为额外的训练数据

  • 同义词替换:随机选择句子中的词,并用其同义词替换

  • 使用句法分析和语义解析技术重新表述句子,保持原意不变

  • 将文本翻译成多种语言后再翻译回原语言,以获得多样化翻译

2.5 更精细的学习率调整策略

  • Noam Scheduler:结合了warmup(预热)阶段和衰减阶段

  • Step Decay:最简单的一种学习率衰减策略,每隔一定数量的epoch,学习率按固定比例衰减

  • Cosine Annealing:学习率随周期性变化,通常从初始值下降到接近零,然后再逐渐上升

2.6 集成训练

训练多个不同初始化或者架构的模型,并使用集成方法(如投票或平均)来产生最终翻译。这可以减少单一模型的过拟合风险,提高翻译的稳定性。

3 个人感受

在整个夏令营第二期的学习过程中,我觉得一定要仔细地阅读datawhale提供的资料,看不懂不必深究,先完整地过一遍,在头脑里形成知识框架,然后再自己去找资料,比如datawhale文档里面会提供一些拓展内容或者相关书籍论文的链接,也可以在csdn、b站等平台上找,因为这些内容的产出者的面向对象和自身水平的不同,所以虽然讲的是同一个东西,但是各有不同,有的会讲的比较深入,有的会照顾到新手容易误解的地方,然后你会发现,在这一过程中,会不断地解决问题并产生新问题,你对所学内容也会了解的更多。

因为我之前没接触过这方面,所以很多代码我也不理解,很多老师讲的方法策略我也没法自己写代码实现,我觉得可能我这种情况尽量搞清楚每一步都在干什么,大概是个什么思路是比较重要的,可能具体怎么用代码实现做不到,但是能理解每一段代码实在通过什么方式实现什么功能就已经会让你学的时候心里面有底儿了。

从我自己的 task3 体验来说,我觉得数据清洗十分重要,在我查看模型对测试集的翻译时,我发现经常有很多的“(笑声)”之类的翻译,甚至几行几行的出现,原因是训练集中有一段话剧的内容,话剧中会使用“张三:(狂笑)你来抓我呀”的形式来描述张三说台词时的神情或者动作,而括号中的内容只有中文有,英文只有人物ZhangSan和he说了what,没有“(smile,san=0)”这一部分,这些不干净的数据对MT(机器翻译)造成了很严重的影响。

我因为最开始BATCH-SIZE=32,导致显存不够,最终测试集的翻译行数不对无法成功提交,在调整为1之后,我发现代码的运行速度明显变慢(n=148363 epochs=10 每个epoch要跑58min),这个参数一般为2的幂,我建议还是把他稍微调大一点比较好,会明显快很多(比如调为4的时候,n=148363 epochs=10 每个epoch要跑15min)

调整head参数时要注意Embed_dim // head,嵌入层数需要是heads的倍数

我觉得一定要边跑边学,学了才能跑,跑了才知道问题出在哪儿,知道问题了才知道该学什么。

最后感谢群里面的大佬耐心的答疑解惑,感谢助教老师的帮助和鼓励,我之前完全没有接触过这方面知识,一直怕自己问的问题太傻而不敢问,但是大家真的很nice

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值