赞
踩
AI初学者,第一次跑代码训练模型实战。有些个人想法可能存在问题,欢迎指正。
基于术语词典干预的机器翻译挑战赛选择以英文为源语言,中文为目标语言的机器翻译。本次大赛除英文到中文的双语数据,还提供英中对照的术语词典。参赛队伍需要基于提供的训练数据样本从多语言机器翻译模型的构建与训练,并基于测试集以及术语词典,提供最终的翻译结果。
数据集:
官方baselineTask01的代码模型是seq2seq,encoder和decoder都是GRU。
Task2添加了注意力机制和数据清洗。
Task3是Transformer。
有几个包需要额外安装:
torchtext
:是一个用于自然语言处理(NLP)任务的库,它提供了丰富的功能,包括数据预处理、词汇构建、序列化和批处理等,特别适合于文本分类、情感分析、机器翻译等任务jieba
:是一个中文分词库,用于将中文文本切分成有意义的词语sacrebleu
:用于评估机器翻译质量的工具,主要通过计算BLEU(Bilingual Evaluation Understudy)得分来衡量生成文本与参考译文之间的相似度!pip install torchtext
!pip install jieba
!pip install sacrebleu
spacy
:是一个强大的自然语言处理库,支持70+语言的分词与训练需要注意的是,使用命令!python -m spacy download en_core_web_trf安装 en_core_web_sm 语言包非常的慢,经常会安装失败,这里我们可以离线安装(去github下载好)。由于en_core_web_sm 对 spacy 的版本有较强的依赖性,你可以使用 pip show spacy 命令在终端查看你的版本,可以看到我的是 3.7.5 版本的 spacy。
github上下好上传到服务器,离线安装(第三行)即可。
!pip install -U pip setuptools wheel -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -U 'spacy[cuda12x,transformers,lookups]' -i https://pypi.tuna.tsinghua.edu.cn/simple
!pip install ../dataset/en_core_web_trf-3.7.3-py3-none-any.whl
src[0]
)torch.save(model.state_dict(), save_path)
生成pt文件)download到本地,后续可以加载模型继续增加epoch训练。报名
→下载数据集和baseline代码
→个人优化修改等
→利用魔搭平台算力训练
→(开发集评估
→)测试集输出翻译结果到文件submit.txt
→下载输出文件并上传到官方测评分数
。
出分后打卡。
自然语言处理(Natural Language Processing,NLP)是语言学与人工智能的分支,试图让计算机能够完成处理语言、理解语言和生成语言等任务。
大致可以将 NLP 任务 分为四类:
机器翻译(Machine Translation,简称MT)是自然语言处理领域的一个重要分支,其目标是将一种语言的文本自动转换为另一种语言的文本。机器翻译的发展可以追溯到20世纪50年代,经历了从基于规则的方法、统计方法到深度学习方法的演变过程。
深度学习中神经网络编码器-解码器模型:
通过循环网络(RNN)对源语言文本进行编码,并生成目标语言翻译结果的过程十分简单。然而,它仅仅使用一个定长的向量 hm编码整个源语言序列。这对于较短的源语言文本没有什么问题,但随着文本序列长度的逐渐加长,单一的一个向量 hm 可能不足以承载源语言序列当中的所有信息。
引入注意力机制的循环机器翻译架构与基于简单循环网络的机器翻译模型大体结构相似,均采用循环神经网络作为编码器与解码器的实现。关键的不同点在于注意力机制的引入使得不再需要把原始文本中的所有必要信息压缩到一个向量当中。引入注意力机制的循环神经网络机器翻译架构如图所示:
传统的 Seq2Seq 模型在解码阶段仅依赖于编码器产生的最后一个隐藏状态,这在处理长序列时效果不佳。注意力机制允许解码器在生成每个输出词时,关注编码器产生的所有中间状态,从而更好地利用源序列的信息。具体来说,给定源语言序列经过编码器输出的向量序列 h 1 , h 2 , h 3 , . . . , h m h_{1},h_{2},h_{3},...,h_{m} h1,h2,h3,...,hm,注意力机制旨在依据解码端翻译的需要,自适应地从这个向量序列中查找对应的信息。
基于循环(上下文的语义依赖是通过维护循环单元中的隐状态实现的,早期的上下文信息容易被遗忘)或卷积(受限的上下文窗口)神经网络的序列到序列建模方法是现存机器翻译任务中的经典方法。然而,它们在建模文本长程依赖方面都存在一定的局限性。
太有名了,这里不多介绍。
Attention is all you need!(https://datawhaler.feishu.cn/wiki/OgQWwkYkviPfpwkE1ZmcXwcWnAh#ZH6cdmbkYoaf1Kx4KgDc5Tfunqe)
Transformer讲解
Transformer20题
在机器翻译领域,BLEU(Bilingual Evaluation Understudy)是一种常用的自动评价指标,用于衡量计算机生成的翻译与一组参考译文之间的相似度。这个指标特别关注n-grams(连续的n个词)的精确匹配,可以被认为是对翻译准确性和流利度的一种统计估计。计算BLEU分数时,首先会统计生成文本中n-grams的频率,然后将这些频率与参考文本中的n-grams进行比较。如果生成的翻译中包含的n-grams与参考译文中出现的相同,则认为是匹配的。最终的BLEU分数是一个介于0到1之间的数值,其中1表示与参考译文完美匹配,而0则表示完全没有匹配。
BLEU-4 特别指的是在计算时考虑四元组(即连续四个词)的匹配情况。
BLEU 评估指标的特点:
可以看出BLEU-4评分严格,我估计最终提交的分数也是1000条数据得分之和,所以一条数据的得分很低很低,自己从0训练这也很正常吧。
审查数据,绝大多数文本序列长度都在100以内,所以MaxSeqLen = 100。多余截断,不足填充pad。
MAX_LENGTH = 100 # 最大句子长度
model.eval()
进入评估模式(不再dropout)。恢复训练记得使用model.train()
进入训练模式。
词嵌入后记得*sqrt(d_model)放缩。(具体原因不是很清楚,上文提到的Transformer20题中有一问,但问答好像不匹配)
# 构造数据集:文本序列 转为 词id序列 class TranslationDataset(Dataset): def __init__(self, data: List[Tuple[List[str], List[str]]], en_vocab, zh_vocab): self.data = data self.en_vocab = en_vocab self.zh_vocab = zh_vocab def __len__(self): return len(self.data) def __getitem__(self, idx): # 数组越界错误,群友的建议(官方初始的样本个数大于真实样本个数,根据报错修改N=len(self.data)=148333即可) if idx >= len(self.data): raise IndexError(f'index {idx} out of range for data with length {len(self.data)}') en, zh = self.data[idx] # 句子开头结尾添加<bos>、<eos>标签。用于标识句子开头和结尾。 en_indices = [self.en_vocab['<bos>']] + [self.en_vocab[token] for token in en] + [self.en_vocab['<eos>']] zh_indices = [self.zh_vocab['<bos>']] + [self.zh_vocab[token] for token in zh] + [self.zh_vocab['<eos>']] return en_indices, zh_indices
# 训练好后测试集中翻译句子函数 def translate_sentence(src_indexes, src_vocab, tgt_vocab, model, device, max_length=50): model.eval() src_tensor = src_indexes.unsqueeze(0).to(device) # 添加批次维度 with torch.no_grad(): encoder_outputs = model.transformer.encoder(model.positional_encoding(model.src_embedding(src_tensor) * math.sqrt(model.d_model))) # 从<bos>开始,依赖上一次token输出,循环输入到model中直到<eos>结束,这部分主要是tokenId trg_indexes = [tgt_vocab['<bos>']] for i in range(max_length): trg_tensor = torch.LongTensor(trg_indexes).unsqueeze(0).to(device) with torch.no_grad(): output = model(src_tensor, trg_tensor) pred_token = output.argmax(2)[:, -1].item() trg_indexes.append(pred_token) if pred_token == tgt_vocab['<eos>']: break # 根据tokenId换成token词汇 trg_tokens = [tgt_vocab.get_itos()[i] for i in trg_indexes] return trg_tokens[1:-1] # 移除<bos>和<eos>标记
保持模型不变,可以考虑改变训练参数N(训练集数据个数)、epoch等。
task1中
baseline默认参数
代码默认训练阶段只取了1000条数据对,提交默认的训练结果评分只有0.2左右。
将N提高到1万条数据对进行训练,epoch保持十轮不变。训练时长为7分钟。分数提高到了1.0224。
teacherForcing机制
通过阅读源代码,了解到训练时采用了概率为0.5的teacherForcing和freeRunning混合的模式。
保持N=1万、epoch=10,修改训练阶段tearcherForcing的概率为0.85(即0.15的概率是freeRunning),评分又提高到了1.444,对比翻译结果文本也可明确得出,tearcherForcing效果会更好。
学习率策略
后来加入了余弦退火的学习率调整策略,分数下降了许多,毕竟训练数据太少模型简单,解释如下。
task3中
学习率策略一开始使用Transformer本身推荐的Noam。但是效果并不好。(可能是代码写错了)
epoch=10,固定学习率时,后期loss下降变缓,所以可以尝试下学习率衰减。
通过输出训练样本发现许多中文语句中包含如(笑)(掌声)
类的词汇。可考虑清洗。但是效果并不好,可能不如保留此类词汇,所以测试集的译文中可能也带有此类词汇。本文最高分等都是采取了正则匹配删除了(..)
。
然后英文文本中去除特殊符号。
zh = re.sub(r'\(.*?\)', '', zh)# 删除中文文本中的`(笑)`等文本
en = re.sub(r'[a-zA-Z0-9.,!?]+', ' ', en)# 去除特殊符号等
...en.lower()...# 全部小写
术语词典策略:
其实经过比较,post_translate方式效果很差,甚至有时还不如不采用术语词典。因为他首先需要术语(人名等)翻译严格准确,而这在从0训练的模型中已经是比较难的了。
另外通过审查词典数据以及观察此方式下有术语词典参与的翻译结果,发现词典中有些“术语”会导致翻译结果更差,比如词典中有“One:一”
这种术语,然后译文中出现很多翻译错误:(seq2seq模型,训练集10000,epoch=10)
如果你想象One下,…
我认为我是One个很大的挑战的故事…
…我会把它放在One起。
初步想法,所以如果想使用这个最简单的策略(在翻译结果中检测并替换回原术语)
One:一
这种术语映射应该是可以删除的。每次训练,保存最佳模型和最新模型。download到本地方便后续增加epoch训练。逐渐增加epoch反复训练。
print(f'Epoch {epoch + already_base_epoch+1}, Learning Rate: {optimizer.param_groups[0]["lr"]}')
模型参数如图:
看群友的意思,可能层数增益不大???,增加epoch还会有所提升。
群友发的三层50epoch比这效果好很多。就是不清楚群内大佬们都用的哪个学习率策略。
Tip:注意每次增加epoch训练前修改path等常量读取相应模型。如果是新实例那么还要pip安装一遍依赖库,而后续执行不再用安装了。
第11~20个epoch:保持参数(只把学习率从1e-4改到2e-4)训练,取消术语词典的译后替换。
可以发现第15轮的valLoss降到了最低,后面有升高,可能发生了过拟合。20epoch又有所降低。
第21~30个epoch,还抱有期望,根据11~20轮loss趋于平缓,这次降低了学习率但还是固定在1e-4,最终发现已经严重过拟合了。应该加上权重衰减试试的。(图中Epoch显示错误,应该是21~30的)
用最小Val. Loss的模型(第24epoch)去跑测试集,分数还是相较于第15轮的有点提升。
最简单的就是调参,将 epochs 调大一点,使用全部训练集,以及调整模型的参数,如head、layers等。如果数据量允许,增加模型的深度(更多的编码器/解码器层)或宽度(更大的隐藏层尺寸),这通常可以提高模型的表达能力和翻译质量,尤其是在处理复杂或专业内容时。
格式化
转换所有文本为小写,确保一致性;标准化日期、数字等格式。
分词
这里使用了使用jieba 对中文进行分词,使用spaCy对英文进行分词。
序列截断和填充
序列截断:限制输入序列的长度,过长的序列可能增加计算成本,同时也可能包含冗余信息。
序列填充:将所有序列填充至相同的长度,便于批量处理。通常使用标记填充。
添加特殊标记
序列开始和结束标记:在序列两端添加<SOS/BOS>(Sequence Start)和<EOS>(Sequence End)标记,帮助模型识别序列的起始和结束。
未知词标记:为不在词汇表中的词添加<UNK>(Unknown)标记,使模型能够处理未见过的词汇。
采用更精细的学习率调度策略(baseline我们使用的是固定学习率):
train loss | test loss | 说明 | 措施 |
---|---|---|---|
不断下降 | 不断下降 | 网络仍在学习 | 最好的情况 |
不断下降 | 趋于不变 | 网络过拟合 | max pool或者正则化 |
趋于不变 | 不断下降 | 数据集100%有问题 | 检查dataset |
趋于不变 | 趋于不变 | 学习遇到瓶颈,需要减小学习率或批量数目 | 减少学习率 |
不断上升 | 不断上升 | 网络结构设计不当,训练超参数设置不当,数据集经过清洗等问题 | 最不好的情况 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。