实验场景:
使用两种文本预训练模型,我想要一个文本经过这两个预训练模型输出两个txt_embedding,而且要求两个txt_embedding的shape一致
问题描述
两个预训练模型的分词策略不同,会导致输入的tokens长度不同,则输出的shape也毫无疑问地不同
复现
text = "he's embedding"
# 两种不同的文本预训练模型的分词器
bert_tokenizer = BertTokenizer.from_pretrained('./pretrain/bert-base-uncased')
spacy_tokenizer = spacy.load("en_core_web_sm").tokenizer
# 分词
bert_token = bert_tokenizer.tokenize(text)
spacy_token = spacy_tokenizer(text)
print(' '.join(bert_token))
print(len(bert_token))
print(spacy_token)
print(len(spacy_token))
两个分词结果
he ' s em ##bed ##ding
6
he's embedding
3
解决方案:
bert_tokenizer 的分词粒度更小,可以将 bert_tokenizer 输出的分词结果以空格拼接起来,作为一个句子再输入进 spacy_token 进行分词,这样就可以保证 bert_tokenizer 和 spacy_token 的分词结果的一致
记录1:
bert_tokenizer 输出的分词结果以空格拼接之前,将 ‘##’ 清洗掉,否则也会影响分词结果。‘##’ 表示是是某词根的词缀
记录2:
测试有报错
RuntimeError: stack expects each tensor to be equal size, but got [100] at entry 0 and [101] at entry 5
这说明还是有分词不一致的情况,直接打印出分词不一致的句子看看为什么
bert_tokenizer = BertTokenizer.from_pretrained('./pretrain/bert-base-uncased')
spacy_tokenizer = spacy.load("en_core_web_sm").tokenizer
reid_raw = read_json('./dataset/CUHK-PEDES/reid_raw.json')
for raw in reid_raw:
for caption in raw['captions']:
bert_token = bert_tokenizer.tokenize(caption)
bert_token = [word if re.match('[#]{2}[\w]*', word) is None else word[2:] for word in bert_token] # 去除掉'##'
bert_txt = ' '.join(bert_token)
spacy_token = spacy_tokenizer(bert_txt)
if len(bert_token) != len(spacy_token):
print(str(raw['id']) + ' - ' + raw['file_path'] + ' - ' + str(len(bert_token)) + ' - ' + str(len(spacy_token)))
print(bert_txt)
print(spacy_token)
然后发现了一些 bert 分了之后 spacy 还会分的词:im、id、ive、cannot、hes、wed,我期望的是 spacy 不能分这些词,会总共有八百多句文本包含这样的词。既然不多,我就直接将词最后一个字母重复一次,即 imm、idd、ivee、cannott、hess、wedd,让 spacy 识别不到这些词从而不做分词。讲究的就是个随意(理论上影响很小)