美文网首页rasa
Rasa学习笔记2--Rasa Core

Rasa学习笔记2--Rasa Core

作者: ColdCoder | 来源:发表于2019-11-06 15:56 被阅读0次

    [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 保存对话状态,包括:

    相关文章

      网友评论

        本文标题:Rasa学习笔记2--Rasa Core

        本文链接:https://www.haomeiwen.com/subject/pluycftx.html