
Rasa2 NLU 架构及源码解析(三)
尊龙时凯
李丹 郑飞 杜昕宸 韩彤 秦帅帅

Rasa是当前智能机器人中最流行的的聊天机器人框架,,,是基于机器学习和自然语言处理技术开发的系统,,,用于构建上下文AI助手和聊天机器人。。。
1.
背景
近年来,,聊天机器人受到了学术界和工业界的广泛关注。。。人工智能技术的快速发展突破了聊天机器人原有的技术瓶颈,,并且实践证明,,,,聊天机器人的使用不仅能够为企业减少一大笔人力成本,,而且能够明显提高工作效率,,国内外多家企业纷纷布局聊天机器人行业。。微软推出了基于情感计算的聊天机器人小冰,,,百度推出了用于交互式搜索的聊天机器人小度,,,进而推动了聊天机器人产品化的发展。。。聊天机器人系统可以看作是机器人产业与“互联网+”的结合,,,符合国家的科研及产业化发展方向。。。。
随着人工智能在银行和金融科技的客户服务方面取得了重大改进,,,客户越来越习惯于获得快速响应。。。金融机构必须全天候回答客户问题和进行交易。。。金融机构业务扩展的加速使人工客服的成本大幅攀升的同时又无法持续满足服务质量,,,,人工智能机器人通过金融机构长期积累的业务经验和数据培训聊天机器人,,,可明显改善客户体验。。。基于上述痛点和需求,,,各类聊天机器人框架应运而生。。。。根据社区活跃度、、、技术的成熟完备度及被引用、、、、点赞等指标,,,,我们采用Rasa作为人机交互对话机器人基本框架。。
2.
Rasa简介
Rasa Open Source有两个主要模块:
●Rasa NLU :用于理解用户消息,,包括意图识别和实体识别。。以pipeline的方式处理用户对话,,可在config.yml中配置。。。
●Rasa Core:主要负责对话管理。。。根据NLU输出的信息、、、以及Tracker记录的历史信息,,,,得到上下文的语境,,,,从而预测用户当前步最可能执行哪一个action。。
其中,,Rasa NLU主要依赖自然语言处理技术,,,,是可以独立的、、与整体框架解耦的模块,,可支持大量NLP前沿技术,,,,以组件的形式,,,,可以灵活与其他开源、、、、自研框架搭配使用。。。
3.
Rasa NLU架构及源码解析
3.3 Custom Component案例
3.3.1自定义组件方式
●拆分方式:将分词、、特征化、、、模型等部分进行解耦,,以TextCNN意图识别模型为例
●整体打包方式:将模型的各部分封装一起,,,,只对外暴露个别接口,,以JointBert意图和实体联合识别模型为例
3.3.2拆分方式
3.3.2.1简介
TextCNN做意图识别,,以拆分的方式进行自定义组件,,以字作为单位,,,不用分词。。。只需拆分成:featurizer和 classifier两部分。。。。
3.3.2.2组件介绍
●Featurizer:
components.user_featurizer.UserFeaturizer
主要功能:构建字典,,,,获取字到id的映射,,得到句子的id 列表。。。。
主要参数:

训练流程:

components的train函数 -> 构建字典:基于最小词频和词典最大容量 -> 遍历所有的训练数据 -> 得到句子的id列表 -> 实例化特征类Features - > message设置
预测流程:

components的process函数 -> 获取句子id列表 -> 实例化Features设置message
●Classifier:
components.user_intent.UserIntentClassifier
主要功能:利用TextCNN进行意图分类
主要参数:

训练流程:

components的train函数 -> 过滤掉空的examples -> 获取字典大小 -> 获取label -> 构建label2id dict-> 构建模型网络 -> model.train
预测流程:

components的process函数 -> 获取句子id list -> 调用predict函数预测 -> _get_ranking 对结果进行排序 -> 保存到message
注意:两个组件之间的依赖关系,,,,相关信息的传递,,,例如:词典大小,,,,无法在classifier中当作参数配置,,,,只能根据featurizer中构建词典的结果得到。。。。这里通过在featurizer中构建字典之后保存到message中,,,在classifier中通过message获取。。。

3.3.2.3使用样例
注意:样例仅配置了NLU部分,,,,训练时使用rasa train nlu,,,测试时使用rasa shell nlu。。。
domain中的意图:

配置文件config:

运行样例:
{
"text": "收不到余额变动提醒怎么办",
"intent": {
"name": "personal_bank",
"confidence": 0.9055530429
},
"entities": [],
"intent_ranking": [
{
"intent": "personal_bank",
"confidence": 0.9055530429
},
{
"intent": "finance_investment",
"confidence": 0.0601818413
},
{
"intent": "personal_finance",
"confidence": 0.033517994
},
{
"intent": "corporate_bank",
"confidence": 0.0006058206
},
{
"intent": "other",
"confidence": 0.0001413494
}
]
}
3.3.3整体打包方式
3.3.3.1简介
JointBert意图和词槽联合识别,,,,以整体打包的方式进行自定义组件,,包放在src/smartintslotpretrain,,,,对外暴露三个函数即可,,分别为:训练train, 预测evals, 模型加载load_model。。
3.3.3.2组件介绍
联合识别组件:
components.jointbert_intent_slot.JointBertIntentSlot
主要功能:进行意图和词槽的联合识别
主要参数:

训练流程:

JointBert作为外部包,,,提供三个接口,,,,训练train, 加载load_model,,,,预测evals。。。。
component的train函数 -> 通过transfer_data进行数据格式转换,,将rasa的nlu数据转化为JointBert所需要的数据格式(具体为:过滤空文本,, auto_mark进行实体标注,,,注意实体标注可能存在group和role的区分,,,save format data 保存为特定格式的文件)-> 调用JointBert的train.train_model进行训练。。
数据转化样例:
nlu.yml中的数据:

转换之后的数据格式:


auto_mark关键代码:
注:这里只标注了存在role的情况,,,,如果存在group可按同样的方式进行添加。。。。

预测流程:

components的process函数 -> 文本和JointBert的模型传入 evals.preditce_online中进行预测,,,得到 intent_dict, slot_dict -> format_entity 格式化成rasa需要的entity格式-> _sort_intent_ranking 对intent进行排序 -> message.set 进行保存结果。。。。
注:format_entity 为 auto_mark的逆过程,,,这两部分需要对齐。。。

以打包方式进行组件编写的方式关键有三点:
●合理的提供模型的三个接口:训练、、、模型加载、、、预测
●rasa数据到模型数据的转换,,不同模型训练源码需要的数据格式都不太相同。。。
●模型预测结果到rasa需要结果格式的转换
3.3.3.3使用样例
注意:样例仅配置了NLU部分,,训练时使用rasa train nlu,,,,测试时使用rasa shell nlu。。。。
domain中的intent和entity:

配置文件config:

运行样例:
{
"text": "今天黄金的价格是多少",
"intent": {
"name": "gold_price",
"confidence": 0.999890089
},
"entities": [
{
"entity": "date",
"value": "今天",
"start": 0,
"end": 2,
"extractor": "JointBertIntentSlot"
}
],
"intent_ranking": [
{
"intents": "gold_price",
"confidence": 0.999890089
},
{
"intents": "UNK",
"confidence": 3.54958e-05
},
{
"intents": "consume_check",
"confidence": 2.15434e-05
},
{
"intents": "currency_exchange",
"confidence": 2.13388e-05
},
{
"intents": "card_requirement",
"confidence": 1.93974e-05
},
{
"intents": "card_meterial",
"confidence": 1.20613e-05
}
]
{
"text": "10美元能兑换多少人民币",
"intent": {
"name": "currency_exchange",
"confidence": 0.9999859333
},
"entities": [
{
"entity": "money",
"value": "10",
"start": 0,
"end": 2,
"extractor": "JointBertIntentSlot"
},
{
"entity": "currency_type",
"value": "美元",
"start": 2,
"end": 4,
"extractor": "JointBertIntentSlot",
"role": "from"
},
{
"entity": "currency_type",
"value": "人民币",
"start": 9,
"end": 12,
"extractor": "JointBertIntentSlot",
"role": "to"
}
],
"intent_ranking": [
{
"intents": "currency_exchange",
"confidence": 0.9999859333
},
{
"intents": "UNK",
"confidence": 4.3686e-06
},
{
"intents": "gold_price",
"confidence": 3.5197e-06
},
{
"intents": "consume_check",
"confidence": 2.3771e-06
},
{
"intents": "card_meterial",
"confidence": 1.879e-06
},
{
"intents": "card_requirement",
"confidence": 1.8156e-06
}
]
}
{
"text": "6.1的黄金价格是多少",
"intent": {
"name": "gold_price",
"confidence": 0.9998868704
},
"entities": [
{
"entity": "date",
"value": "6.1",
"start": 0,
"end": 3,
"extractor": "JointBertIntentSlot"
}
],
"intent_ranking": [
{
"intents": "gold_price",
"confidence": 0.9998868704
},
{
"intents": "UNK",
"confidence": 3.81634e-05
},
{
"intents": "consume_check",
"confidence": 3.09156e-05
},
{
"intents": "currency_exchange",
"confidence": 2.11939e-05
},
{
"intents": "card_requirement",
"confidence": 1.48951e-05
},
{
"intents": "card_meterial",
"confidence": 7.9957e-06
}
]
}
3.3.4优缺点

References
1. Rasa官方文档
https://rasa.com/docs/rasa/2.x/components
2. diet介绍
https://www.yiyibooks.cn/nlp/diet/index.html
3. spaCy官方文档
https://spacy.io/api/architectures#parser
4. Greedy transition-based parsing
https://explosion.ai/blog/parsing-english-in-python
5. spaCy v2.0命名实体识别解析
https://www.bilibili.com/video/av16282127
6.NLU自定义组件
https://puluwen.github.io/2018/12/rasa-nlu/

