【多轮对话】从微软源码看用户模拟器构建
用户模拟器是在任务型多轮对话场景中,用于模拟用户在指定目标下多轮对话过程,可以用于生成对话数据,以及通过强化的方式训练系统决策。在具体的任务型场景需要定义有哪些用户行为、用户意图、用户可能说的槽位等,而之后用户模拟器是怎么来推进对话过程的呢?
论文:A User Simulator for Task-Completion Dialogues
代码地址: https://github.com/MiuLab/TC-Bot
本文首先简单介绍用户模拟器方法,再从源码细节分析具体多轮对话中的用户模拟器应该如何构建。
用户模拟器
用户行为user action
首先第一步就是确定用户会有哪些action,以及action对应的意图、槽位有哪些,这个与具体的任务有关。但是很对最近的论文都是复用剑桥提出的DSTC2/3(餐馆预订)中对用户action的定义,那么他们定义了哪些呢:
act |
包含的slot |
描述 |
---|---|---|
inform |
槽值(s, v), ... |
用户告知系统的值,例如:饭店是xx,厂家是xxx |
request |
槽位名s |
用户询问系统的槽位,例如:xx药的厂家有哪些? |
ack |
None |
肯定(这里和确认很像,但是这里是会话的肯定,肯定的返回结果),例如:Okay, what’s the phone number of that place?” |
affirm |
None |
认可/是的 |
negate |
None |
否定/不是 |
reqalts |
None |
其他、以上都不是 |
confirm |
one slot (s, v) |
s是可以告知的槽位,v是对应的值,例如:这个药品是同仁堂生产的吗? |
deny |
one slot (s, v) |
s是可以告知的槽位,v是对应的值,例如:不要国产的格列卫 |
null |
None |
空、非当前域、未理解 |
reqmore |
None |
还有哪些、还有更多的剂型吗 |
thankyou |
|
致谢 |
help |
None |
你有什么功能、你可以做什么、我可以问什么 |
repeat |
None |
重复一下、你说什么 |
bye |
None |
|
Hello |
None |
|
例子:
[{"act":"confirm", "slots":[["area", "centre"]]},
{"act":"request", "slots":[["slot", "phone"]]},
{"act":"deny", "slots":[["area","north"]]},
{"act":"hello", "slots":[]},]
其实affirm和confirm和合并,negate和deny也可以合并
确定了user action,以及任务的槽位和意图之后,就可以开始构建基于Agenda-Based的用户模拟器了。
Agenda-Based的用户模拟方法
Agenda-Based主要通过生成用户目标,然后将其通过出栈的形式来生成对话流,主要包含以下模块:
- 用户目标生成:这部分可以基于数据库随机生成,目标也包含inform_slots、request_slots。生成用户目标之后,
- 用户行为(action)产生:
- 首轮:会有一些条件,比如必须要有餐馆名字等。
- 非首轮:结合系统的问询说出目标中的某一个或多个槽位
- 会话状态:这个是模拟器内部的会话状态:
- 历史信息:用户保存说了哪些槽位,哪些槽位没有说。
- 会话状态:失败、成功、还没结束
- NLG模块:将用户action转化为自然语言。
主函数run.py
通过主函数中DM(run.py)来完成agent和user之间的对话,state_tracker记录并结合KB结果给agent作为其infrom。两者交互通过action(包含request,infrom,diaact等),并非自然语言。agent、user、act_set、slot_set、state_tracker、user_action、reward、episode_over。
也可以利用user bot在线训练agent,然后agent与真实用户交互来继续在线训练。
state_tracker
state_tracker.py中的state_tracker类包含:
- Current_slots:
- Agent_request_slots: agent必须要知道value的slots;
- Infrom_slot: 当前获得的inform_slots;
- Request_slots: 需要向用户提问的slots;
- history_dictionaries: 当前对话每轮记录。
- kb_helper:类,可以返回可用的movie list。
- movie_dictionaries: 电影数据库。
- turn_count: 轮次计数。
本文主要介绍模拟器,state_tracker这块感兴趣可以自行细读。
user smilator代码
用户模拟器代码:usersim_rule.py,其中有一个RuleSimulator类,对于其输入、输出为:
输入
首次对话,调用initialize_episode(),直接会输出,无需输入。
对话过程中调用next(),需要输入system_action, map形式,key包含:
- diaact: action,例如 request, infrom
- infrom_slots: 包含imform的slot-value
- request_slots: 包含slot-‘UNK’
- turn: 当前对话轮次
举例子:system_action: {'request_slots': {'date': 'UNK'}, 'turn': 3, 'speaker': 'agent', 'inform_slots': {}, 'diaact': 'request'}
输出
自然语言。例如:which restaurant and what is the address for breaskfast for 10 people on tomorrow morning
输出是自然语言还是结构化的字段(user action),可以按需更改,因为按照M2M对话产生对话的outline也可以是user action。
构造函数
- 域字典:包含域的字典和对应项;如movie_dict,包含key有actor,city,date等,value是其对应的取值。(语料,kb)
- act_set:user的action type,如closing,inform,等。
- slot_set:域对应的slot type,如actor,date等。
- start_set:根据语料生成的usr goal sample。包含inform_slots、request_slots。包含test,train,valid等。
- params:参数,包含
- max_turn最大轮次、
- slot_err_probability:(s+d+i/c+s+d)槽位错误概率
- slot_err_mode:错误模式
- intent_err_probability:意图错误概率
simulator_run_mode、(0 for dia-act; 1 for NL; 2 for no output)simulator_act_level:0 for dia_act level; 1 for NL level- learning_phase(all, train, test)
- NLG模板。
params举例:{'max_turn': 20, 'slot_err_mode': 0, 'slot_err_probability': 0.05, 'intent_err_probability': 0.05, 'simulator_act_level': 0, 'learning_phase': 'all', 'simulator_run_mode': 0}
。
初始化initialize_episode
记录状态的数据结构: history_slots, insorm_slots, request_slot, rest_slot都为空, turn(int)=0
Episode_over、dialog_status。
goal产生(_sample_goal):从start_set随机选一条记录。其中ticket设为UNK
_sample_action:初始action选择,包含以下:
- diaact: 从配置文件的start_dia_acts中随机选一个(只有request)。
- Infrom_slot: 如果有moviename,则第一轮必出现,再选一个其他的infrom。
- agenda:将其他slot和request slot加入到rest_slot中。
- Request_slot: ticket在最后请求,优先随机选其他request,
根据action调用NLG生成自然语言,加入到action'nl'中。方法:模板,对于agent和usr每类request和infrom都有相应模板。lstm_decoder。
很多逻辑冗余,例如state'request_slots'刚添加,就判空。以及一些不可能出现的情况(上层已经判断过)。
输出:user_action,包含diaact、infrom_slots、request_slots、turn、nl。
Next User action(next函数)
输入:DM输入system_action, 包含request_slot, inform_slots等信息。
输出:response_action
更新信息:state'turn'加2,dialog_status。turn超过最大次数,则state'diaact'='closing'。更新history_slots,清空inform_slots。
对于不同的sys_act,回应不同:
- system request:从sys_action的request中选取第一个slot。如果request为空,则在rest_slot中随机选一个slot。
- 如果在infrom_slot中,将其对应键值保存到state'infrom_slot'中,state'diaact'='infrom', 并从rest_slots中移除,清空state'request_slots',(因为其代表本次request值)
- 如果在request_slot中,state'diaact'='request', 并将slot填入到state'request_slot'。
- 待修改:并且发送如果还有infrom,则全部取出到state'infrom_slot',并将这些infrom从rest_slot中移除。这样做是防止user infrom没说完,系统就返回了结果。
- 否则为donot care,(既不在infrom也不再request)
- Response_inform: 回应系统的inform action.
- System_action中包含键taskcomplete,则转换下一个diaact=thanks,如果其值为no_value_match,则将ticket的value设为no_value;否则检查system_action的slot-value和goal中是否一致,不一致则constraint_check为0,diaact=deny。
- 如果不包含taskcomplete,将infrom加入到state'history_slots',并比对infrom与goal中slot-value是否一致。一致则更新state。
- 如果value不一致,可以选择再说一遍不一致的slot-value,或者直接state'diaact'=deny,结束会话。
- 如果slot不在goal中,是donot care的slot,不管,发起state'request_slots'中请求,如果其为空的则在rest_slots中选一个,如果rest_slots也为空的,则请求ticket。
- multiple_choice:回应系统提供slot的可选择value,user diaact变为inform。将slot从rest_slot移除。
- system inform_slot在user inform slot中,返回inform中的值。
- 如果在user request slot中,那么随机选择一个答案。并将其从request_slot中移除。
- 如果都不在,返回i do not care。
- Response_confirm:
- 如果rest_slot不为空,则随机选取一个slot。
- 否则返回state'diaact' = "thanks"
- thanks:轮次结束,决定对话状态,返回相应奖励,对话成功条件:
- rest_slot中无剩余、
- history_slot无空值并且和goal中一致。
- system_action返回的ticket不为空。
加入噪声corrupt
- 以slot_err_probability概率产生错误的slot-value,slot_err_mode=0,则替换value;mode=1则以1/3概率替换value,1/3概率替换slot和value,1/3删除slot-value;mode=2, 替换slot和value; mode=3, 删除slot和value。
- 以intent_err_probability随机改变user_action'diaact'。
模拟agent SLU识别错误、交互噪声的情况,增加鲁棒性,数据集也会扩充。
经过NLG。
奖励:(DM)
- Status = sucess, R=2*max_turn;
- status= Failed, R= -max_turn;
- Else: R=-1;
名词解释
S = (A,G) , goal G, G=(c, r),c是要求,r是用户请求获得什么。还需要加入intent信息。agenda A堆栈类型(未完成),对话系统解决一个出栈一个。
User goal
- inform_slot: 用户的限制的slot-value;其中又分为Require slots和Optional slots;
- request_slot: 用户请求的slot。
数据生成方式:
- 从语料中用户第一轮的数对话提取槽位。
- 所有用户的所有回合的槽位。
将user goal生成database,然后对话开始时候从中随机采样生成user goal。
User action
首轮:会有特殊限制,如一般都会包含request,至少一个infrom slot,movie名如果知道首轮会出现。
对话过程中,包含一个类堆的agenda,过程中会出栈和入栈(agenda出现没有的slot)。
Dialogue Status
no_outcome_yet, success(回答所有requestable slots,通过NLU来识别,并给出正确的票), failure。还会限制对话轮次,超出算失败。并决定返回奖励。
NLU
intent和slots联合训练,使用Seq2Seq方法,数据为全部对话action和utterance,IOB的slot标签和intent标签
NLG
- 模板。
- 模型:Seq2Seq,输入是dialog action,生成类模板的句子。
对于dialog act,如果找到了规则模板,则输出模板,否则使用模型的输出。
扩展
最近也有很多使用模型来做用户模拟器的,这里简单提一下,后续有机会细说。主要还是通过数据驱动的模型方法来自动学习用户,通过对话历史、user profile、task schema作为输入,预测下一轮应该说什么,主要有:
- 基于统计建模
- Collaboration-based User Simulation for Goal-oriented Dialog Systems
- A stochastic model of human machine interaction for learning dialog strategies
- 有监督:
- Neural User Simulation for Corpus-based Policy Optimization for Spoken Dialogue Systems
- A Sequence-to-Sequence Model for User Simulation in Spoken Dialogue Systems
- 强化:Iterative Policy Learning in End-to-End Trainable Task-Oriented Neural Dialog Models
- 逆强化:User Simulation in Dialogue Systems using Inverse Reinforcement Learning
另外看了一篇感觉比较合理google AI: USER MODELING FOR TASK ORIENTED DIALOGUES,可以简单看下他的模型图
Reference
- A User Simulator for Task-Completion Dialogues
- Agenda-Based User Simulation for Bootstrapping a POMDP Dialogue System
- A Sequence-to-Sequence Model for User Simulation in Spoken Dialogue Systems(