【对话系统】天气对话机器人(二)----- 关于Rasa踩过的那些坑

Rasa 是一个机器人对话开源框架,目前版本还在不断迭代中。之前做的一个天气对话机器人就是基于它的,这次我想来记录一下用Rasa创建对话系统所遇到的坑。

持续更新的github:https://github.com/Emmonss/weather_online_chatbot

演示界面:http://111.230.201.198:8000/

官方文档:https://rasa.com/docs/core/quickstart/  (Chrome可转中文)

1. 安装

 话说在Linux下rasa的安装也不是啥难事,但是一定要注意选择版本,由于其版本迭代过快,很多老版本里的函数和接口到了新版本就不能用了。曾经我安装的是0.11.4的,发现学习的demo里面各种报错,求爷爷告奶奶,查官方文档,上github问官方开发人员,搞来搞去,还是把版本降到了0.10.4,才没有了那么多的事情。

根据github上某基于rasa高星中文对话项目做铺垫,安装rasa有以下几个步骤:

PS:本人墙裂建议在Python虚拟环境下安装,省得出什么岔子弄起来麻烦。

1. pip安装rasa本体:

pip install rasa_core==0.10.4 -i https://pypi.tuna.tsinghua.edu.cn/simple/

后面根据具体需求定制版本,不然直接安装最新版本,出了什么问题网上找不到答案就只能去问开发人员了。另外国内墙裂推荐使用清华库,速度快,不做作,无time out。

2.安装最新版本的scikit-learn

pip install -U scikit-learn sklearn-crfsuite

这个主要是因为rasa的pipline设置。加上了sklearn,对于每一个实体识别或者是意图识别就有了概率,很好用。

3.安装mitle

pip install git+https://github.com/mit-nlp/MITIE.git

这也是在linux下安装方便的原因,Windows下安装,不光在安装本体的时候要考虑vc++的问题,mitle也是很难安装的。

2.关于pipline:

这个也是官方推荐版本:

pipeline:
- name: "nlp_mitie"
  model: "data/total_word_feature_extractor.dat"
- name: "tokenizer_jieba"
  user_dicts: "data/new_word.txt"
- name: "ner_mitie"
- name: "ner_synonyms"
- name: "intent_entity_featurizer_regex"
- name: "intent_featurizer_mitie"
- name: "intent_classifier_sklearn"

由于从某大佬那里拷贝来了total_word_feature_extractor.dat,所以mitle下下来我貌似就没用过,反正用起来也麻烦,数据也不好找。这个文件在我的github项目中也有,就是上文的指定路径。

关于实体命名识别,目前现有的轮子中我也没研究过到底哪个好,但是不管怎么样总比自己写BiLSTM-CRF训练数据来的快来的好吧。按照官方文档中的例子,jieba分词+mitles识别,对于小的数据集识别准确率在98%往上,应该够用。

3.关于NLU

NLU即nature language understanding ,自然语言理解部分。目的是从自然语言中抽取我们需要的实体以及识别相关的意图。

Rasa框架,我们写一行命令,就可以训练自己的NLU系统了。

首先要准备数据集,并写成相应的json文件格式,例如天气,就要准备大量的如下数据集:

{
        "text": "北京今天的天气怎么样",
        "intent": "request_search_blurry_weather",
        "entities": [
          {
            "end": 2,
            "entity": "location",
            "value": "北京",
            "start": 0
          },
          {
            "end": 4,
            "entity": "blurry_time",
            "value": "今天",
            "start": 2
          },
          {
            "end": 7,
            "entity": "weather_dot",
            "value": "天气",
            "start": 5
          }
        ]
  }

其中text是文本,intent是文本意图,entities是实体,还要标注实体属性以及实体在文本中出现的位置。

如果觉得这样麻烦,可以将数据写成方便的格式,然后脚本转换过来。

我这里有一个别人的转换脚本,只要将数据集写成如下格式:

text,intent,location,weather_dot
合肥的天气怎么样|utter_ask_time|合肥,天气
北京的天气怎么样|utter_ask_time|北京,天气
查一下南京的天气怎么样|utter_ask_time|南京,天气
问一下上海的天气|utter_ask_time|上海,天气

然后脚本文件是trainsfer_raw_to_rasa.py(不是我写的)运行就可以了。

训练nlu的指令:

python -m rasa_nlu.train --data ./data/weather_file_new.json \
    --config chatbot_config.yml \
    --path models \
    --fixed_model_name demo \
    --project ivr

之前定义的pipline文件就是要用在这里的,把这个写成.sh文件,直接运行就行了,注意保存模型的路径和格式。

测试:

测试官方文档有说明的,好像是一个http服务器,然后开两个终端,一个负责运行,另一个负责发送,就能搞定了。

注意:

nlu的数据样本要适中,不要太多也不要太少。比如我训练的天气对话,我将全国900多个地名都放进去了,但是其他的数据就比较少,结果地名可以识别,也会将一些不在数据集中的文本标注为地名。。。。。

4.关于自定义策略

自定义策略其和模板策略官方已经讲了很清楚了,这部分唯一我踩的坑就是版本的坑。。。。。

因为在11.4版本中,自定义对话在config文件中可以直接声明名称而不是调用方法了,这点我还是比较认可的。但是你要新开一个服务器是什么鬼啊,模板对话一个服务器,自定义对话是另外一个服务器。

当时为了解决这个问题头都要秃了,后来发现还是降版本实用。这件事情告诉我们,永远不要轻易跟进一个正在迭代的框架。否则有的问题你只能跨洋去问其开发人员了。

5.关于对话训练集

这部分是我比较烦恼的,这个对话的训练集吧,它长这个样子:

## Generated Story -342089912898217228
* inform_location{"location": "\u518d\u89c1\uff01"}
    - slot{"location": "\u518d\u89c1\uff01"}
    - utter_goodbye
    - action_slot_reset
    - reset_slots
* inform_location{"location": "\uff01"}
    - slot{"location": "\uff01"}
    - utter_goodbye
    - action_slot_reset
    - reset_slots
* thanks{"location": "\uff01"}
    - slot{"location": "\uff01"}
    - utter_thanks
    - action_slot_reset
    - reset_slots
    - export

乍一看,条理清晰,有条不紊,什么意图对应什么操作什么回应,一目了然,除了将汉字写成acii码以外,毫无缺点。

但是,这东西怎么手工写呢??????

官网倒是给我安排得明明白白的:“几百个Story是一个很好的开始。。。。”   WTF!!

好在Rasa 有一个在线学习功能,说白了就是你一句一句的输入,再选择相应的操作,将你的这些操作保存下来,就形成了新的数据,除非你在选择操作的时候手残,不然不会出什么岔子(本人经常手残,然后Ctrl+C重新来)

不过坑爹的也在这里,要知道,策略对话是讲逻辑的。拿天气来举例子:

你现在已经知道了地名,时间和天气标记三个实体,那么此时你应该执行查询天气系统,将这三作为查询根据。

或者你知道了地名和天气标记,这个要问“您要查的是哪天呢亲?”

或者乱七八槽写了一堆没有天气标记,这时就要装傻“不好意思,不会”。

但是rasa是讲概率的,对于每个操作它会根据当前的实体槽和上一句话的意图,对所有的意图做概率(其实就是一个LSTM+sofmax)检测,取概率最大的操作。

对于小场景这个没啥的,但是大场景有时会出现逻辑混乱的情况 ,比如没有时间它也会去查询天气。这时候你也没办法去删除story来控制逻辑,因为你根本不知道哪些stroy使其逻辑出了偏差。这是候,根据样本中数量越多者为王的道理,你只能一遍遍地教他:1+1=2,1-1=0。说白了就是扩大正确的样本集。

另外,在写模板回复或者自定义方法的时候最好细分一下,越详细越好,这样出偏差的概率就会小很多。

比如我一开始有没有时间都是跳到查询天气,在函数内部发现时间槽为空,然后返回一句话询问天气,等有了天气槽再访问该函数。这样的偏差概率就很大。后来我改成了时间槽为空先返回模板回答问时间,然后等三个条件满了再问。这样就没偏差了。

越大的场景下,越要细分,自定义操作要简单和多,这样不容易出岔子。

 

你可能感兴趣的:(对话系统)