解决使用bert encoder出现的一系列问题

本篇文章仅是针对自己最近项目中遇到的问题进行梳理沉淀,并不能复现解决相同的问题。但是或许可以提供一个解决问题的思路:尝试分析原始数据是否符合模型要求

背景

基于presumm和hiersumm两个文本摘要项目,希望使用bert encoder对数据进行编码。

任务详情

  1. 输入:一串文本数据
  2. 输入格式:二进制.pt文件
  3. 模型:bert,Transformer
  4. 输出:一串文本数据

问题起因

模型在加载数据时报错,报错内容如下

TypeError: forward() missing 1 required positional argument: 'attention_mask'

具体操作步骤

  1. 将文本数据(src,tgt,src_txt,tgt_txt)转换为.pt文件
  2. 将模型encoder设置为bert encoder
  3. 模型加载.pt文件
  4. 出现上述报错

分析

遇到该错误就在找到底attention_mask在哪,查看bert源码发现是模型中forward()函数,因为forward()函数是模型在迭代(如梯度下降、反向传播)才会调用,并不知道如何将attention_mask怎么传入。因此也尝试发帖问了一些问题(问题描述的不够清楚,因为自己本身也没意识到问题根源在哪里,报错信息只是个表象)
CSDN问题链接
知乎提问链接
Stackoverflow问题链接

bert encoder实现及定位问题

  1. bert encoder实现:presumm→model_builder.py→AbsSummarizer()→forward()中使用的是 self.bert(src)替代 self.encoder(src)
  2. 如果使用bert(src)需要数据中有segs,mask_src等向量;上文提到的attention_mask实际是bert中segs和mask_src等向量
  3. 这些向量来自于presumm→data_builder()→BertData()

因此当前任务为将原始数据转换为BertData,解决进度如下

  1. 对标presumm,将hiersumm→data2json处理成 汉字分字 的格式,去掉token转换和保留str(增加args参数和判断条件)
  2. 将presumm→prepro→data_builder.py & utils.py & preprocess.py拷贝到hiersumm中
  3. 将presumm→others相关文件拷贝到hiersumm中,并在开头注释
  4. 修改preprocess2bert.py参数→log_file地址
  5. hiersumm→trainer_builder.py→204行需要完善
  6. 将presumm→trainer.py→_gradient_accumulation(对bert数据的segs,mask处理)拷贝到hiersumm中
  7. 将presumm→model_builder.py→class AbsSummarizer copy到hiersumm中
  8. Transformer Decoder 出现维度问题
  9. 查看hiersumm→model_builder.py→AbsSummarizer→forward(),发现得到的dec_state维度结果与presumm维度结果不同;进而发现是init_decoder_state方法定义不同

在转换BertData的过程中,又出现了以前跑通的代码报错的问题(还是没有完全解耦,写代码的痛啊)

训练bertData数据

  1. 将bertData数据丢到presumm上跑,模型:bert_emd+bert_enc+trans_dec
  2. 格式化bertData要在hiersumm→先运行dataprocess2json.py(取消token和save_str)→再运行preprocess.py→即可得到bert.pt数据;丢给presumm运行模型
  3. 发现设置train_step=795,但会loading 若干次数据停不下来,与该参数有关
parser.add_argument("-accum_count", default=5, type=int)  # 设置每accum_count个数据,进行一次梯度下降,原始默认为5改为1

训练部分的代码已经跑通了,在进行测试的时候,又出现了问题

解决test问题

bert_emb+bert_encoder+transformer_decoder是在presumm项目中训练、测试;而bert_data是需要在hiersumm项目中处理

  1. 测试时,发现原始项目使用bert-base-uncased字典,需要替换成bert-base-chinese
  2. bert-base-chinese字典缺少[unused0]token,导致BOS对应出错,未登录词
symbols = {'BOS': tokenizer.vocab['[unused0]'], 'EOS': tokenizer.vocab['[unused1]'],
           'PAD': tokenizer.vocab['[PAD]'], 'EOQ': tokenizer.vocab['[unused2]']}
  1. 因此采用[unused3]替换[unused0]
  2. 发现需要在hiersumm→处理bert_data时,也要替换为[unused3]
    使用bert-base-chinese文件,里面添加unused0;
    过程中发现unk的问题
  3. 考虑将bert_data中,tgt_str和src_str,中文汉字之间的空格去掉
    空格分开,在代码中可以便于split切分,如果去掉空格,后面的split也要跟着修改,增加任务栏,暂时考虑不去掉空格
  4. 需要将Bert_data和bertencoder使用同一个vocab文件,因此尝试将presumm的bertdata跑通,实现将变体字文件格式化成bert.pt;然后在presumm中使用bert encoder
    presumm处理bertData,src为list导致preprocess_variant方法中途退出
  5. 由于在hiersumm的vocab中增加了unused0,需要重新跑一下train和test代码

test跑通后,发现训练效果非常不好,继续分析解决问题

解决test输出效果不好的问题

  1. 将变体字格式化为bert_data,迁移到了presumm上
  2. 跑通preprocess.py代码
  3. 发现模型效果不好,翻译结果很多[UNK]
  4. 经过仔细检查发现是preprocess.py处理时,就有很多中文字符识别不出来
  5. 发现是“PreSumm_MDS/src/others/tokenization.py”146,147行将vocab_path写成本地的bert-base-uncase了,未使用中文vocab
  6. 写成本地vocab原因是因为presumm的preprocess.py调用了多线程,而多线程线上访问huggingface的bert-base-chinese失败,具体失败的原因没有排查出来,stackoverflow上有提到是和macos有关

    https://stackoverflow.com/questions/30669659/multiproccesing-and-error-the-process-has-forked-and-you-cannot-use-this-corefou
    https://stackoverflow.com/questions/50168647/multiprocessing-causes-python-to-crash-and-gives-an-error-may-have-been-in-progr
    https://blog.csdn.net/caroline_wendy/article/details/109314089

  7. 干脆全部使用本地vocab,已经把preprocess.py部分的vocab替换成本地路径了
  8. 看一下huggingface正版的bert-base-chinese.txt
  9. model_builder.py还需要继续替换,model用的是.bin模型,而不是vocab

使用了中文本地vocab发现中文结果仍然不够理想

解决test输出结果不够理想的问题

  1. 发现 hiersumm→随机emb/bert_emb+trans_enc/bert_enc+trans_dec效果不太好
  2. 仔细观察了一下原始数据的token和str,并从token转换为str,再从str转换为token。发现数据对应有误。原因是之前在vocab中新增了一个token [unused0],导致数据错位
  3. 重新生成json→.pt→train→test,数据恢复正常

总结

自此本问题彻底解决,花费了两周的时间,自上到下一步步分析问题,解决问题,解决本问题又牵扯出了很多其他问题(也影响到了之前写好的代码),不错最终还是逐步攻破了

你可能感兴趣的:(NLP,bert,深度学习,pytorch,transformer,nlp)