如何让Rasa NLU识别未训练新实体?支持词表匹配或相对位置识别
针对你遇到的CRF实体提取器只能识别训练过的精确词汇,无法处理未见过的新词(比如新品牌名)的问题,我给你几个实用的解决方案,结合你的场景应该能快速解决:
1. 优化CRF提取器特征,让它依赖上下文而非精确词汇
CRF实体提取器的识别逻辑完全依赖配置的特征,默认特征可能过于聚焦词汇本身,导致未见过的词无法匹配。你可以调整特征配置,让模型更多关注上下文位置、词汇形态(前缀/后缀)、词性这些通用特征,从而学会“识别buy a后面的名词作为watch实体”这类模式,而非死记硬背训练过的品牌名。
修改你的config.yml中的CRF相关配置,示例如下:
language: en pipeline: - name: WhitespaceTokenizer - name: LexicalSyntacticFeaturizer features: # 关注当前词的形态特征(小写、首字母大写、前缀后缀等) - ["low", "title", "upper", "shape", "prefix5", "suffix5"] # 关注前后2个词的上下文形态特征,强化位置关联 - ["low", "title", "upper"]: [-2, -1, 1, 2] # 如果集成了spaCy,可添加词性特征,增强语义判断 - ["pos", "tag"]: [-2, -1, 0, 1, 2] - name: CRFEntityExtractor # 调整正则化参数,减少模型对训练词汇的过拟合 C: 1.0
调整后,模型会学习到“在buy a之后的名词短语属于watch实体”的通用模式,即使是未训练过的Citizen M1也能被识别。
2. 添加领域专属词汇列表(类似LUIS的短语列表)
如果你的手表品牌是可控的(可以提前整理所有可能的品牌/型号),可以用Rasa的LookupTableEntityExtractor实现类似LUIS短语列表的功能,直接匹配列表中的词汇作为实体。
步骤1:创建词汇列表文件
在data/lookups/目录下新建watch_brands.txt,每行写入一个品牌/型号:
Casio SX56 Citizen M1 Rolex Submariner Seiko SKX007
步骤2:在配置中添加Lookup提取器
修改config.yml的pipeline,加入LookupTableEntityExtractor:
pipeline: # 保留原有基础组件 - name: WhitespaceTokenizer - name: LexicalSyntacticFeaturizer # 添加Lookup提取器,匹配自定义词汇列表 - name: LookupTableEntityExtractor entities: ["watch"] lookup_tables: - name: watch_brands file: data/lookups/watch_brands.txt entity: watch # 保留CRF提取器,实现多提取器互补 - name: CRFEntityExtractor
这样,只要输入中的词汇在你的列表里,就会被识别为watch实体,完全不需要依赖训练数据中的精确匹配。
额外推荐:尝试DIETClassifier(更适配未见过的实体)
如果你的场景需要识别大量未提前枚举的新词,推荐用Rasa的DIETClassifier替换CRF。DIET结合了Transformer语义模型和CRF,能捕捉词汇的语义上下文,对未见过的实体识别效果远优于传统CRF。配置示例:
language: en pipeline: - name: WhitespaceTokenizer - name: LanguageModelFeaturizer model_name: "bert" model_weights: "bert-base-uncased" - name: DIETClassifier epochs: 100 entity_recognition: true intent_classification: true
DIET会自动学习“购买意图下的产品名称”这类语义模式,即使是完全陌生的品牌名也能准确识别。
内容的提问来源于stack exchange,提问作者avinex

