Rasa学习笔记2--Rasa Core

[TOC]

Rasa学习笔记2--Rasa Core

1. 概念介绍

首先引出Rasa的设计理念:
Learning from real conversations is more important than designing hypothetical ones,我觉得这是非常重要的事情,我们在构建自己的bot的时候 ,我们往往想绞尽脑汁来排列组合出各种意图的story,但是在实际应用中,我们发现不管我们的story库有多丰富,还是会有大量的unhappy path无法处理,所以我们应该想方设法从真实的对话中来抽象sotry。

1.1 Stories

sotry就是Rasa用来训练对话管理模型的数据集。格式如下:*开头的表示某一轮用户输入的intent和slot信息。-开头的表示分配给bot对应的action。

## greet + location/price + cuisine + num people    <!-- name of the story - just for debugging -->
* greet
   - action_ask_howcanhelp
* inform{"location": "rome", "price": "cheap"}  <!-- user utterance, in format intent{entities} -->
   - action_on_it
   - action_ask_cuisine
* inform{"cuisine": "spanish"}
   - action_ask_numpeople        <!-- action that the bot should execute -->
* inform{"people": "six"}
   - action_ack_dosearch

1.2 Domains

定义了整个系统需要操作的所有元素,包括:intent,slot,entity,template,actions。

1.3 Actions

定了了bot具体执行动作。在Rasa中定了四种action,包括:

  1. Utterance actions,直接返回template中定义的话术。
  2. Retrieval actions,闲聊(small talk)和FAQ(small questions)的方式。
  3. Custom actions,用户自定义。用户自定义的action,需要发布action server,然后通过HTTP请求来获取执行action的答案。
  4. Defailt actions,比如:action_listen, action_restart, action_default_fallback等

1.4 Policies

所谓policy就是bot在对话中的每一步决定要执行哪一个action。我们可以在config.yml中配置多个policies,Agent会根据policy的执行优先级来选择最终的action。
Max_history ,bot再决定action时会向前考虑的对话轮数。
Data Augmentation, 在训练模型是,Rasa会从stories.md中随机取出sotry拼接一起看作一个story,用来做数据扩充。

agent决定action的一般原则是选择这些policy给的得分最大的action,但是当两个policy给出了相同的最高得分,这时候就需要根据policy的priority来下决定采取哪一个policy的结果。在Rasa中推荐的设置是:

5. FormPolicy
4. FallbackPolicy and TwoStageFallbackPolicy
3. MemoizationPolicy and AugmentedMemoizationPolicy
2. MappingPolicy
1. EmbeddingPolicy, KerasPolicy, and SklearnPolicy

1.4.1 KarasPolicy

构造训练数据和模型输入:

  1. 对于每个story,会先封装成一个TrackerWithCachedStates对象实例,里面包括:
    sender_id: story标识
    _states: 这个story可以生成的所有对话状态的组合,内容是[最大max_history的状态+一下个状态]
    domain
    slots,此story包含的slots和值
    latest_message,
    latest_action_name,
    latest_bot_utterance,
    event,包括ActionExcuted,UserUttered,SlotSet等,封装domain中的所有内容

  2. 构造对话状态与预测action
    分别是:trackers_as_states和trackers_as_actions,他们是一一对应的关系,去重之后会有n个组合。其中:
    trackers_as_states,所有max_history状态的组合,比如:



    trackers_as_actions,预测的action,比如对应上面005:


  3. 将上述结果编码,构造模型输入X,Y
    X:shape(n, max_history, feature_num),其中feature_num=envent的数量,使用one-hot编码,为1的位置,表示当前state的Event。因为Event的定义就是用来描述对话中的所有内容。

Y: shape(n, actins_num),因为policy只预测action,所以Y的one-hot用来表示预测的actin来。

1.4.2 Embedding Policy (Transformer Embedding Dialogue Policy)

1.5 Forms

现看一个使用Form实现的对话情形:


所谓form就是slot filling,是我们在实现任务型对话的一般思路,及:首先定义一些必须槽位,每个槽位会构建一个反问话术,然后bot会顺序反问用户,收集信息,直到填满所有槽位。FormAction就是来实现这个功能。

名词解释:happy path就是指你问用户什么信息,用户就配个回答给bot这个信息。

比如下面这个happy path:

## happy path
* greet
    - utter_greet
* request_restaurant
    - restaurant_form
    - form{"name": "restaurant_form"}
    - form{"name": null}
    - utter_slots_values
* thankyou
    - utter_noworries

当用户意图是request_restaurant,bot会接着执行restaurant_form action。form{"name": "restaurant_form"}会激活这个form,form{"name": null}会关闭这个form。对于unhappy path的情形,bot可以执行任意其他的action,只要这个form还是active的。

我们可以使用Rasa提供的sdk来定制自己的FormAction,需要实现下面三个方法:

  • name(),aciton的名字
  • required_slots(),列出来所有必须的槽位
  • submit()方法,当所有槽位被填充,此action产生的结果或者执行的动作。

一旦form action被激活,FormPolicy会对应的被激活,FormPolicy非常简单,它预测的一下个动作永远还是form action。

定制化slot mapping
有时候我们填充槽位不一定只是用抽取到的slot value,也可以是多种形式的回答。比如当bot问:"您是要坐在外面吗?",用户可能的回答有:“是的”/"不是"/“我更喜欢坐在里面”。这几个回答都是不同的意图或者是相同实体中的其他value,但却都是正确的回答,所以,我们要将这些答案mapping到这一轮的槽位上。
FormAction可以支持将yes/no以或者自由文本映射到slot中,只需要实现slot_mappings()方法。

def slot_mappings(self):
    # type: () -> Dict[Text: Union[Dict, List[Dict]]]
    return { "outdoor_seating": [self.from_entity(entity="seating"),
                      self.from_intent(intent='affirm', value=True),
                      self.from_intent(intent='deny', value=False)]}

如上所示,我们将所有mapping策略函数封装在list中,赋值给目的槽位"outdoor_seating"。详细来讲:

  1. from_entity,将抽取出来的"seating"(seating表示实体)的value填充到outdoor_seating
  2. from_intent,如果识别的意图是affirm,True填充到outdoor_seating,反之,False填充到outdoor_seating。
    还有其他的复制策略函数,from_trigger_intent,from_text。

slot value validation
有时候我们需要对用户输入的slot value进行个性化的确认,我们可以在FormAction中使用 validate_{slot-name} 方法来实现。当然,我们也可以在validate的过程中来终止form,即放validate方法返回self.deactivate(),或者重新反问。

处理unhappy path
在FormAction执行过程中,如果用户不配合,比如:问其他问题、闲聊或者改变了主意,这时候form会引起ActionExecutionRejection,所以,你需要采取措施不让这种情形产生。比如,
处理闲聊情形的story如下:

## chitchat
* request_restaurant
    - restaurant_form
    - form{"name": "restaurant_form"}
* chitchat
    - utter_chitchat
    - restaurant_form
    - form{"name": null}

当用户改变注意,不在询问最初的需求,这时候bot就不能继续询问最初form的槽位。便可以使用action_deactivate_form来关闭此form:

## chitchat
* request_restaurant
    - restaurant_form
    - form{"name": "restaurant_form"}
* stop
    - utter_ask_continue
* deny
    - action_deactivate_form
    - form{"name": null}

处理待条件的slot的逻辑
这个功能是为了让对话更加灵活和有个性化,比如某个人回答要吃希腊菜,bot可以反问他是不是需要露天的座位。可以在FormAction中通过required_slots()来实现:

@staticmethod
def required_slots(tracker) -> List[Text]:
   """A list of required slots that the form has to fill"""

   if tracker.get_slot('cuisine') == 'greek':
     return ["cuisine", "num_people", "outdoor_seating",
             "preferences", "feedback"]
   else:
     return ["cuisine", "num_people",
             "preferences", "feedback"]

1.6 检索动作(retrieval action)

这个是Rasa在实验的新功能,主要作用是为了在解决闲聊(small talk)和Faq(simple question)问题时,可以简化构建story,因为这些都是单轮的对话,可以尝试通过一个aciton解决掉。

比如,我们可以不在使用下面这些story:

## weather
* ask_weather
   - utter_ask_weather

## introduction
* ask_name
   - utter_introduce_myself

...

而是用一个chitchat意图来将上面所有意图包装起来:

## chitchat
* chitchat
   - respond_chitchat

然后retrival action使用NLU组件中的response selector模型来学习和预测结果。

训练数据

准备NLU训练数据:

## intent: chitchat/ask_name
- what's your name
- who are you?
- what are you called?

## intent: chitchat/ask_weather
- how's weather?
- is it sunny where you are?

首先所有上面的sample将被放在一起用来训练意图识别模型用来预测chitchat类别。然后通过一个'/'符号来分隔出来的后缀就是对应的答案,将作为训练模型的label,这其实就是一个问题-答案的qq模型。

然后,在另外一个response.md文件中准备response语料:

## ask name
* chitchat/ask_name
    - my name is Sara, Rasa's documentation bot!
    
## ask weather
* chitchat/ask_weather
    - it's always sunny where I live

1.7 interactive actions

1.8 Fallback Action

当NLU得分低于某个阈值或者policy预测低于某个阈值,需要进行对话回退,也就是尝试让用户重新表达。有下面两种用法:

  • 使用FallbackPolicy
policies:
 - name: "FallbackPolicy"
    nlu_threshold: 0.4
    core_threshold: 0.3
    fallback_action_name: "action_default_fallback"

action_default_fallback这个action会对应执行模板中定义的utter_default来反问客户。

  • 使用TwoStageFallbackPolicy
policies:
 - name: TwoStageFallbackPolicy
    nlu_threshold: 0.3
    core_threshold: 0.3
    fallback_core_action_name: "action_default_fallback"
    fallback_nlu_action_name: "action_default_fallback"
    deny_suggestion_intent_name: "out_of_scope"

当用户输入低于nlu阈值,TwoStageFallbackPolicy会采用多个阶段来处理:

  • 如果用户输入低于nlu阈值,bot执行 action_default_ask_affirmation让用户确认输入: 如果用户说yes,则把输入看作大于阈值的输入, sotry继续。如果用户否认,让用户重新输入。
  • 执行action_default_ask_rephrase让用户重新输入:如果用户用户重新输入的句子大于阈值,则sotry继续。如果用户输入再一次低于阈值,再次让用户确定意图。
  • 执行action_default_ask_affirmation让用户二次确认:如果用户确认,则story继续。如果用户否认,用户输入会被认定为'deny_suggestion_intent_name',就会执行最终的召回动作fallback_nlu_action_name(比如转人工)。

1.8 Knowledge Base Actions

目前Rasa使用这个action主要解决两个问题:

  • 当用户需要具体问某个实体的信息和属性
  • 解决指代问题
    如下面这个例子:


1.8.1 Knowledge Base

rasa_sdk中提供数据库加载的抽象类:InMemoryKnowledgeBase,直接:knowledge_base = InMemoryKnowledgeBase("knowledge_base_data.json")来构造实例。文档中提供的一个数据库例子如下:

{
    "restaurant": [
        {
            "id": 0,
            "name": "Donath",
            "cuisine": "Italian",
            "outside-seating": true,
            "price-range": "mid-range"
        },
        {
            "id": 1,
            "name": "Berlin Burrito Company",
            "cuisine": "Mexican",
            "outside-seating": false,
            "price-range": "cheap"
        },
        {
            "id": 2,
            "name": "I due forni",
            "cuisine": "Italian",
            "outside-seating": true,
            "price-range": "mid-range"
        }
    ],
    "hotel": [
        {
            "id": 0,
            "name": "Hilton",
            "price-range": "expensive",
            "breakfast-included": true,
            "city": "Berlin",
            "free-wifi": true,
            "star-rating": 5,
            "swimming-pool": true
        },
        {
            "id": 1,
            "name": "Hilton",
            "price-range": "expensive",
            "breakfast-included": true,
            "city": "Frankfurt am Main",
            "free-wifi": true,
            "star-rating": 4,
            "swimming-pool": false
        },
        {
            "id": 2,
            "name": "B&B",
            "price-range": "mid-range",
            "breakfast-included": false,
            "city": "Berlin",
            "free-wifi": false,
            "star-rating": 1,
            "swimming-pool": false
        },
    ]}

这是一个餐馆的数据,其中id和name一般都是要配置的。当然我们也可以定制自己的数据库。

1.8.2 准备NLU data

首先定义一个新的意图query_knowledge_base来表示“查询数据库”的用户意图,然后bot对应的执行``ActionQueryKnowledgeBase `动作,目前rasa中可以解决的两种分类是:1. 列举出用户指定属性的所有对象。2. 用户想知道某个对象的某个属性。对于上述的餐馆领域,可以构造如下的nlu数据:

## intent:query_knowledge_base
- what [restaurants](object_type:restaurant) can you recommend?
- list some [restaurants](object_type:restaurant)
- can you name some [restaurants](object_type:restaurant) please?
- can you show me some [restaurant](object_type:restaurant) options
- list [German](cuisine) [restaurants](object_type:restaurant)
- do you have any [mexican](cuisine) [restaurants](object_type:restaurant)?
- do you know the [price range](attribute:price-range) of [that one](mention)?
- what [cuisine](attribute) is [it](mention)?
- do you know what [cuisine](attribute) the [last one](mention:LAST) has?
- does the [first one](mention:1) have [outside seating](attribute:outside-seating)?
- what is the [price range](attribute:price-range) of [Berlin Burrito Company](restaurant)?
- what about [I due forni](restaurant)?
- can you tell me the [price range](attribute) of [that restaurant](mention)?
- what [cuisine](attribute) do [they](mention) have?
 ...

其中:
object_type: 标注数据库中的某个实体。
mention: 标注指代。
attribute: 数据库中包含的属性。
另外,使用synonyms来将数据中标记的单词映射到数据库中使用的标准格式,比如:'restaurants'->'restaurant'。
上面有提到目前rasa可以解决的两种情景:

  1. 从数据库中查询用户指定属性的所有对象。
    当用户想要从数据库中查询对象,问句中必须包含某个对象,即'object_type',同时需要指定某些属性。
    比如:
    What Italian restaurant options in Berlin do I have?
    用户想得到'object_type'='restaurant', 条件是{'cuisine'='Italian','city'='Berlin'},然后action就通过这两个条件来筛选符合的对象。
    当然想要解析上面的问句需要如下的标注:
    What [Italian](cuisine) [restaurant](object_type) options in [Berlin](city) do I have?.
  2. 用户想知道某个对象的某个属性。
    这种情形,比如:
    What is the cuisine of Berlin Burrito Company?
    用户想知道'Berlin Burrito Company'的'cuisine'。需要的标注如下:
    What is the [cuisine](attribute) of [Berlin Burrito Company](restaurant)?

1.8.3 解决指代

rasa中定义两种指代,1. 顺序指代(ordinal mentions),比如:'the first one',2. 普通指代,比如'it'。

  • 顺序指代
    在bot输出轮,当给用户是一系列象列,可在解析用户回答的时候,可以使用一个顺序指代映射来解决用户问题中的指代词。这个顺序指代映射需要在KnowledgeBase类中定义:
{
    "1": lambda l: l[0],
    "2": lambda l: l[1],
    "3": lambda l: l[2],
    "4": lambda l: l[3],
    "5": lambda l: l[4],
    "6": lambda l: l[5],
    "7": lambda l: l[6],
    "8": lambda l: l[7],
    "9": lambda l: l[8],
    "10": lambda l: l[9],
    "ANY": lambda l: random.choice(list),
    "LAST": lambda l: l[-1],
}

key就是对应到nlu标注数据中的mention后面的值,比如:does the [first one](mention:1) have [outside seating](attribute:outside-seating)?

  • 普通指代
    rasa的处理方案是将上文识最近别到的对象作为指代的对象。

  • 编写ActionQueryKnowledgeBase

2. 代码结构分析和源码学习

  • DialogueStateTracker 保存对话状态,包括:
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345