1. 程式人生 > >【對話系統】天氣對話機器人(二)----- 關於Rasa踩過的那些坑

【對話系統】天氣對話機器人(二)----- 關於Rasa踩過的那些坑

Rasa 是一個機器人對話開源框架,目前版本還在不斷迭代中。之前做的一個天氣對話機器人就是基於它的,這次我想來記錄一下用Rasa建立對話系統所遇到的坑。

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。說白了就是擴大正確的樣本集。

另外,在寫模板回覆或者自定義方法的時候最好細分一下,越詳細越好,這樣出偏差的概率就會小很多。

比如我一開始有沒有時間都是跳到查詢天氣,在函式內部發現時間槽為空,然後返回一句話詢問天氣,等有了天氣槽再訪問該函式。這樣的偏差概率就很大。後來我改成了時間槽為空先返回模板回答問時間,然後等三個條件滿了再問。這樣就沒偏差了。

越大的場景下,越要細分,自定義操作要簡單和多,這樣不容易出岔子。