NLTK三元语言模型生成大量</s>标记问题求助
问题原因与解决方法
为什么会生成大量</s>标记?
这是因为padded_everygram_pipeline会自动给每个句子添加<s>(句首标记)和</s>(句尾标记),用于构建n元语法的完整上下文。当三元模型生成到句尾的</s>后,后续的上下文会变成[前一个词, </s>]——而你的语料里根本不存在以</s>作为第二个词的三元组(自然语言中句子结束后不会有后续词汇),因此MLE模型只能反复输出概率最高的</s>;一旦生成第一个</s>,接下来的上下文变为[</s>, </s>],语料里完全没有对应的后续词汇,模型就会陷入无限输出</s>的循环。
而二元、一元模型不会出现这个问题,原因在于:
- 一元模型不依赖上下文,会从全量词汇中按概率随机选择,不会被
</s>的上下文困住; - 二元模型的上下文是单个词,当生成
</s>后,上下文变为</s>,语料里没有对应的二元组,但二元模型会自动回退到一元模型的概率分布,因此不会持续输出</s>。
解决方法
方法1:生成时过滤</s>标记
修改生成逻辑,跳过</s>直到凑够指定数量的有效词汇:
generated = [] while len(generated) < 50: word = lm_medea.generate() if word != "</s>": generated.append(word) print(" ".join(generated))
方法2:利用generate的stop_words参数
NLTK的generate方法支持stop_words参数,生成到指定词汇时会停止。如果不需要严格生成50个词,只想避免</s>,可以直接传入:
for word in lm_medea.generate(50, stop_words=["</s>"]): print(word, end=" ")
注意:如果模型在生成50个词前就遇到</s>,会提前停止,因此这个方法更适合对生成数量要求不严格的场景。
方法3:手动预处理语料(不推荐)
如果不想让模型学习到</s>标记,可以放弃padded_everygram_pipeline,手动处理语料。但这样会丢失n元语法的句首/句尾上下文,严重影响模型的生成连贯性,因此不建议采用。
内容的提问来源于stack exchange,提问作者Eloragh Espie
相关产品推荐
相关产品推荐

