Transformer架构出来已经三年多了,从最开始的论文,到BERT、GPT,再到现在各种预训练模型层出不穷,Transformer已经成为NLP领域的标配。我用了三年Transformer,从最开始的看不懂论文,到后来能自己实现、调优、落地,踩了不少坑,也积累了一些经验。今天分享一下我用Transformer的一些心得和体会。
先说说背景。我是做NLP(自然语言处理)的,2017年Transformer论文出来的时候,我就开始关注了。那时候,NLP领域主流的还是RNN(循环神经网络)和LSTM,注意力机制也有,但都是作为RNN的辅助。Transformer论文提出了一种全新的架构,完全不用RNN,只用注意力机制,就能取得很好的效果,而且训练速度更快,并行性更好。
说实话,第一次看Transformer论文的时候,我没看懂。什么自注意力、多头注意力、位置编码、前馈网络,这些概念混在一起,看得我头大。而且,那时候也没有现成的代码和教程,只能自己啃论文,啃了很久才慢慢搞懂。
后来,2018年BERT出来了,Transformer开始火起来,各种教程、代码、预训练模型层出不穷,我也开始在实际项目中使用Transformer。从最开始的用现成的BERT做微调,到后来自己实现Transformer,再到后来用Transformer做各种任务,比如文本分类、命名实体识别、机器翻译、文本生成等,用了三年,踩了不少坑,也积累了一些经验。
今天就把这些经验和心得分享出来,希望能帮到正在学习和使用Transformer的朋友。
一、Transformer到底好在哪里?
用了三年Transformer,我最深的感受是:Transformer确实是一个革命性的架构,它彻底改变了NLP领域。和传统的RNN/LSTM相比,Transformer有以下几个优势:
1. 并行性好,训练速度快
这是Transformer最大的优势。RNN是串行的,处理一个句子的时候,必须一个词一个词地处理,后面的词要等前面的词处理完才能开始,无法并行。而Transformer是并行的,处理一个句子的时候,所有的词可以同时处理,不需要等待,所以训练速度快很多。
尤其是在GPU上,并行计算的优势更加明显。用RNN训练一个大模型,可能要训练几个星期;用Transformer,可能几天就训练完了。训练速度的提升,意味着可以用更大的模型、更多的数据,这也是为什么最近几年大模型层出不穷的原因之一。
2. 长距离依赖建模能力强
RNN的另一个问题是长距离依赖建模能力弱。因为RNN是串行的,信息要一步步传递,距离远了,信息就容易丢失或者被稀释。虽然LSTM和GRU通过门控机制缓解了这个问题,但对于很长的序列,效果还是不理想。
而Transformer用自注意力机制,可以直接建模任意两个词之间的关系,不管距离多远,都能直接建立联系,不需要一步步传递。所以,Transformer的长距离依赖建模能力比RNN强很多。
比如,在机器翻译任务中,一个很长的句子,源语言的第一个词,可能和目标语言的最后一个词有对应关系。用RNN的话,这种长距离的对应关系很难建模;用Transformer的话,就可以直接建模,效果好很多。
3. 灵活性高,能做各种任务
Transformer的架构很灵活,只要改一改输入和输出,就能做各种NLP任务,比如文本分类、命名实体识别、机器翻译、文本生成、问答系统等。而且,Transformer的预训练+微调的范式,让一个预训练模型可以适配各种下游任务,不需要为每个任务单独训练一个模型。
这种灵活性和通用性,是RNN比不了的。以前,做不同的任务,要用不同的模型架构,调参也很麻烦。现在,用Transformer,一个预训练模型,微调一下,就能做各种任务,大大降低了NLP的门槛。
4. 可解释性相对较好
虽然深度学习模型整体的可解释性都不好,但Transformer的注意力权重,至少能给我们一些解释。我们可以通过观察注意力权重,看到模型在做预测的时候,关注了哪些词,哪些词是重要的。这对于理解模型的行为、调试模型、做错误分析,都很有帮助。
当然,注意力权重也不是完全可靠的解释,有研究表明,注意力权重和模型的决策不一定完全对应。但至少,它给了我们一个窗口,让我们能看到模型内部的一些情况,这比RNN的黑盒要好一些。
二、学习Transformer的一些建议
很多人学习Transformer的时候,会觉得很难,看不懂论文,搞不懂原理。我自己也是这样过来的,这里给一些学习建议。
1. 先搞懂注意力机制,再看Transformer
Transformer的核心是自注意力机制,所以,在学Transformer之前,先把注意力机制搞懂。注意力机制最早是用在机器翻译中的,用来在解码的时候,关注源语言句子中相关的部分。
可以先学一下Bahdanau注意力和Luong注意力,这两种是比较基础的注意力机制,搞懂了它们,再学自注意力就容易多了。
自注意力和普通注意力的区别在于,普通注意力是源语言和目标语言之间的注意力,而自注意力是同一个句子内部的注意力,每个词都和句子中的其他词计算注意力。搞懂了这个区别,自注意力就不难理解了。
2. 不要只看论文,要结合代码看
Transformer的论文写得比较简洁,很多细节没有说清楚,只看论文的话,很容易看不懂。建议结合代码来看,现在有很多开源的Transformer实现,比如Tensor2Tensor、fairseq、HuggingFace的transformers库等,可以找一个比较简洁的实现,一行一行地看,结合论文的公式,理解每个部分的作用。
我自己就是这样学的。那时候,我找了一个比较简洁的PyTorch实现,一行一行地看,看不懂的地方就去查论文,查资料,花了大概一周的时间,终于把整个实现搞懂了。搞懂了代码之后,再回头看论文,就觉得清晰多了。
3. 自己动手实现一个简单的Transformer
看懂了论文和代码之后,最好自己动手实现一个简单的Transformer,不用太复杂,能跑通一个小任务就行。比如,实现一个简单的机器翻译模型,或者一个文本分类模型。
自己动手实现的过程中,你会遇到很多问题,比如维度对不上、梯度消失、loss不下降等。解决这些问题的过程,就是深入理解的过程。而且,自己实现过一遍之后,对Transformer的理解会更深刻,以后用现成的框架的时候,也知道里面在做什么,调参的时候也更有方向。
我自己就是在实现了一个简单的Transformer之后,才真正搞懂了Transformer的原理。以前,很多地方都是似懂非懂,自己实现一遍之后,就都清楚了。
4. 从用开始,不要一开始就纠结原理
如果你只是想在项目中用Transformer,而不是做研究,那可以从用开始,不要一开始就纠结原理。现在有很多成熟的框架,比如HuggingFace的transformers库,封装得很好,几行代码就能用BERT、GPT等预训练模型。你可以先用起来,在实际项目中用,用熟了之后,再慢慢去了解原理。
很多人一开始就想把原理搞懂,结果看了很久论文也没看懂,就放弃了。其实,没必要一开始就搞懂所有原理,可以先用起来,在使用的过程中,慢慢理解。就像我们用手机,不需要先搞懂手机的原理,先用起来,用着用着就懂了。
三、使用Transformer的一些坑和经验
用了三年Transformer,踩了不少坑,这里分享一些常见的坑和经验。
坑1:学习率太大,导致训练不稳定
这是一个很常见的坑。Transformer的训练,对学习率很敏感。学习率太大,会导致loss震荡,甚至不收敛;学习率太小,训练速度太慢,而且容易陷入局部最优。
Transformer论文中用的是warmup + 余弦退火的学习率策略,先把学习率从0线性增加到峰值,然后再慢慢降下来。这个策略很重要,尤其是在训练大模型的时候。warmup可以避免训练初期的不稳定,余弦退火可以让模型在训练后期收敛到更好的最优解。
我自己的经验是,学习率的峰值,一般设为1e-4到5e-4之间,具体要看模型大小和batch size。模型越大,batch size越大,学习率可以适当大一些。warmup的步数,一般设为总训练步数的5%到10%。
如果是微调预训练模型,学习率要小很多,一般设为2e-5到5e-5之间,因为预训练模型已经训练得很好了,不需要太大的学习率,太大了会把预训练的参数破坏掉。
坑2:位置编码的问题
Transformer本身没有位置信息,因为自注意力是置换不变的,也就是说,把句子中的词打乱顺序,自注意力的结果是一样的。所以,需要给输入加上位置编码,让模型知道词的顺序。
位置编码有两种方式,一种是Transformer论文中的正弦余弦位置编码,另一种是可学习的位置编码。两种方式各有优缺点,正弦余弦位置编码是固定的,不需要学习,可以外推到更长的序列;可学习的位置编码是学习出来的,效果可能更好一些,但不能外推到训练时没见过的长度。
我自己的经验是,对于一般的任务,两种位置编码的效果差不多,用哪种都可以。但如果你的序列长度变化很大,或者需要处理比训练时更长的序列,建议用正弦余弦位置编码,或者用相对位置编码。
另外,需要注意的是,位置编码的维度要和词嵌入的维度一致,而且要加在词嵌入之后,再输入到Transformer中。
坑3:mask的问题
Transformer中有两种mask,一种是padding mask,用来把padding的位置mask掉,不让模型关注padding的部分;另一种是sequence mask(也叫look-ahead mask),用在解码器的自注意力中,防止模型看到未来的信息。
这两种mask很容易搞混,而且如果mask写错了,模型的效果会很差,甚至训练不出来。我自己就踩过这个坑,一开始把mask写错了,结果模型训练了很久,loss都不下降,后来查了很久,才发现是mask的问题。
我的建议是,写mask的时候,一定要仔细,搞清楚每个位置应该mask还是不应该mask。可以写一个小测试,打印出mask矩阵,看看是不是正确的。另外,注意mask的维度,一般是(batchsize, 1, 1, seqlen)或者(batchsize, 1, seqlen, seq_len),要和注意力分数的维度对应上。
坑4:梯度爆炸和梯度消失
虽然Transformer用了LayerNorm和残差连接,缓解了梯度消失和梯度爆炸的问题,但在训练深层Transformer的时候,还是可能会遇到这些问题。
比如,训练很深的Transformer(比如24层、48层)的时候,梯度可能会爆炸,导致loss变成NaN。这时候,可以用梯度裁剪(gradient clipping),把梯度的范数限制在一个范围内,防止梯度爆炸。
另外,还可以用一些优化技巧,比如学习率warmup、残差连接的缩放、初始化策略等,来稳定训练。
我自己的经验是,训练深层Transformer的时候,一定要用梯度裁剪,一般把max_norm设为1.0。而且,学习率不要太大,要用warmup策略。如果还是不稳定,可以适当减小学习率,或者减小batch size。
坑5:数据预处理的问题
Transformer对数据预处理也很敏感。比如,分词的方式,会影响模型的效果。现在一般用BPE(Byte Pair Encoding)或者WordPiece分词,这种分词方式可以很好地处理未登录词,而且词表大小适中。
另外,数据的清洗也很重要。如果训练数据中有很多噪声,比如乱码、重复、错误标注等,会影响模型的效果。所以,在训练之前,一定要好好清洗数据,去掉噪声,保证数据的质量。
还有,数据的长度分布也要注意。如果数据中有很多很长的序列,会导致训练很慢,而且显存占用很大。可以适当截断太长的序列,或者用分桶的方式,把长度相近的序列放在一个batch里,提高训练效率。
坑6:显存不够用
Transformer模型一般都比较大,训练的时候很占显存。尤其是用大batch size、长序列的时候,显存很容易不够用。
我自己的经验是,可以用以下几种方法来节省显存:
- 减小batch size。这是最直接的方法,但batch size太小会影响训练效果。
- 梯度累积(gradient accumulation)。把多个小batch的梯度累积起来,相当于用了大batch size,而显存占用和小batch一样。
- 混合精度训练(mixed precision training)。用半精度(FP16)来训练,显存占用可以减少一半,而且训练速度也更快。现在的GPU基本都支持混合精度训练。
- 梯度检查点(gradient checkpointing)。在前向传播的时候,不保存所有的中间激活值,而是在反向传播的时候重新计算,这样可以节省显存,但会增加计算量。
- 模型并行(model parallelism)。把模型分到多个GPU上,每个GPU只存一部分参数。这个比较复杂,一般只有特别大的模型才需要。
我自己一般用混合精度训练+梯度累积,基本上能满足大部分需求。如果还是不够,再考虑梯度检查点和模型并行。
四、微调预训练模型的一些经验
现在大部分人用Transformer,都是用预训练模型(比如BERT、GPT等)做微调,而不是从头训练。微调预训练模型,也有一些经验和技巧。
1. 选择合适的预训练模型
现在预训练模型很多,不同的模型适合不同的任务。比如,BERT适合理解类任务(文本分类、命名实体识别、问答等),GPT适合生成类任务(文本生成、对话等),RoBERTa是BERT的改进版,效果更好,ALBERT是轻量级的BERT,参数少,训练快。
在选择预训练模型的时候,要根据自己的任务和数据来选。如果是中文任务,要选中文预训练模型,比如BERT-base-chinese、RoBERTa-wwm-ext等。如果数据是特定领域的(比如医疗、法律、金融),可以选在特定领域预训练过的模型,效果会更好。
另外,还要考虑模型大小和算力。如果算力有限,可以选小一点的模型,比如BERT-base,或者更小的DistilBERT、TinyBERT等。如果算力充足,而且追求最好的效果,可以选大一点的模型,比如BERT-large,或者更大的模型。
2. 冻结底层,微调上层
微调预训练模型的时候,不一定所有层都要微调。可以冻结底层的参数,只微调上层的参数,这样可以节省显存,加快训练速度,而且可以防止把预训练的底层特征破坏掉。
一般来说,底层的参数学到的是通用的语言特征,比如词性、语法等,这些特征对大部分任务都适用,不需要微调。上层的参数学到的是更抽象的特征,和具体任务更相关,需要微调。
我自己的经验是,如果任务数据比较少,可以冻结更多的层,只微调最后几层;如果任务数据比较多,可以微调整个模型。当然,具体冻结多少层,要根据任务和数据来定,可以做实验对比一下。
3. 学习率要小,用分层学习率
微调预训练模型的时候,学习率要比从头训练小很多,一般设为2e-5到5e-5之间。因为预训练模型已经训练得很好了,参数已经在一个比较好的位置了,如果学习率太大,会把这些好的参数破坏掉,导致效果变差。
另外,还可以用分层学习率(discriminative learning rates),给不同的层设置不同的学习率。底层的学习率小一些,上层的学习率大一些。因为底层的参数比较通用,不需要太大的调整;上层的参数和任务更相关,需要更大的调整。
我自己的经验是,分层学习率一般能提升一点效果,但不是特别明显。如果追求最好的效果,可以试试;如果嫌麻烦,用统一的小学习率也可以。
4. 数据增强
如果任务数据比较少,可以用数据增强的方法,增加数据量,提升模型的泛化能力。NLP的数据增强方法有很多,比如同义词替换、回译、随机删除、随机交换等。
但要注意,数据增强不能改变标签的含义。比如,情感分类任务,同义词替换的时候,不能把情感词替换成相反的词,不然标签就错了。
我自己的经验是,数据增强在数据少的时候效果比较明显,数据多的时候效果不明显。而且,数据增强的方法要根据任务来选,不是所有方法都适合所有任务。
5. 早停和模型选择
微调预训练模型的时候,很容易过拟合,尤其是在数据少的时候。所以,一定要用验证集做早停(early stopping),当验证集的指标不再提升的时候,就停止训练,防止过拟合。
另外,要保存验证集指标最好的模型,而不是最后一个epoch的模型。因为训练后期,模型可能会过拟合,验证集指标会下降,所以最好的模型可能在中间的某个epoch。
我自己的经验是,微调预训练模型,一般训练3到5个epoch就够了,不需要训练太多。太多了容易过拟合。而且,一定要用早停,保存最好的模型。
五、对Transformer未来的一些看法
用了三年Transformer,我对这个架构的未来也有一些看法。
1. 模型会越来越大,但也会越来越高效
现在的趋势是,模型越来越大,从BERT的几亿参数,到GPT-3的1750亿参数,再到现在的万亿参数模型。大模型确实效果好,能做很多以前做不了的事情。但大模型的训练和推理成本也很高,不是所有人都能用得起。
所以,未来的另一个趋势是,模型会越来越高效。比如,模型压缩(剪枝、量化、蒸馏)、高效的注意力机制(稀疏注意力、线性注意力)、更高效的训练方法等。这些技术,可以让小模型也能达到大模型的效果,或者让大模型的训练和推理更高效。
我觉得,未来会是大模型和小模型并存的局面。大模型用来做通用的预训练,或者做复杂的任务;小模型用来做特定的任务,或者部署在资源有限的设备上。
2. 多模态是未来的方向
现在的Transformer,主要还是用在文本上。但未来,多模态会是一个重要的方向。Transformer的架构很灵活,可以很容易地扩展到图像、音频、视频等模态。现在已经有很多多模态的Transformer模型了,比如ViT(图像Transformer)、CLIP(图文对比学习)、DALL-E(文本生成图像)等,效果都很好。
未来,多模态Transformer会越来越成熟,能处理更多模态的数据,做更多跨模态的任务,比如图文生成、视频理解、语音识别等。这会让AI的能力更全面,也能应用到更多的场景。
3. Transformer可能不是终点
虽然Transformer现在很火,几乎成了NLP的标配,但它可能不是终点。Transformer也有一些问题,比如自注意力的计算复杂度是O(n²),序列长了之后,计算量很大;比如,Transformer的可解释性还是不够好;比如,Transformer需要大量的数据和算力才能训练好。
现在已经有一些研究,在探索比Transformer更好的架构,比如线性注意力、稀疏注意力、状态空间模型(如Mamba)等。这些新的架构,可能在某些方面比Transformer更好,比如计算效率更高、长序列建模能力更强。
当然,现在说这些新架构能取代Transformer还为时过早,Transformer还有很强的生命力。但技术总是在发展的,未来可能会有更好的架构出现,取代Transformer,就像Transformer取代RNN一样。
作为从业者,我们要保持开放的心态,关注新技术的发展,不断学习,不断进步。
六、写在最后
用了三年Transformer,从最开始的看不懂论文,到现在能熟练使用和调优,我深刻地感受到了技术的发展和进步。Transformer确实是一个革命性的架构,它彻底改变了NLP领域,也推动了整个AI领域的发展。
但技术的发展是永无止境的,Transformer也不是终点。作为从业者,我们要不断学习,不断进步,跟上技术的发展。同时,也要保持理性,不要盲目跟风,要根据自己的任务和需求,选择合适的技术和方法。
希望我的这些经验和心得,能帮到正在学习和使用Transformer的朋友。如果有什么不同的看法,欢迎在评论区交流。
最后,想说的是,深度学习这个领域,变化很快,新技术层出不穷。但不管技术怎么变,基础的东西是不变的,比如数学基础、编程能力、解决问题的思路。把基础打牢,再去学新技术,就会容易很多。与大家共勉。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录