聊天机器人-DST模块

这部分也被称为Belief Tracking。

首先要思考为什么需要DST?

这个问题是因为我们需要一种对话状态,或者至少我们觉得对话流程有一种状态性的东西比较合适。

1. 什么是DST

对话状态追踪(Dialogue State Tracker),对DST来说包括只读部分和可变部分。DST模块以当前的用户动作、前轮对话状态和相应的系统动作作为输入,输出是DST模块判定得到的当前对话状态。

对话状态的表示(DST-State Representation)通常由以下3部分构成:

  1. 本轮对话过程中的用户动作。
  2. 目前为止的槽位填充情况。
  3. 对话历史。

1.1 什么是对DST只读的

  • 例如用户行为是用户决定的,系统应该不改变(但是可以增强或修正),所以对DST来说就是只读的。
  • 例如系统行为是DPL发出的,是系统做过的决策,这个自然也是只读的。

1.2 什么是对DST可变的

  • 对于基于帧(Frame)的对话系统来说,可变部分基本上已经定义了帧中的内容。
  • 例如我们设计这样的一个对话帧,用来实现返回天气预报。我们假设这个系统只能回答某一天某个城市的天气,那么就有两个必要变量:城市和日期。

那么我们设计语义帧可以这么做:

{
      "city": null,
      "date": null
}

也就是说当用户inform了系统的所有必要条件,这里是city和date之后,并且DST也填充更新了语义帧,那么DPL应该就能给出对应的天气回答。

其中槽位填充情况通常是最重要的状态表示指标。

2. 槽位设计

我们知道,由于语音识别不准确或是自然语言本身存在歧义性等原因,NLU模块的识别结果往往与真实情况存在一定的误差。所以,NLU模块的输出往往是带概率的,即每一个可能的结果有一个相应的置信程度。由此,DST在判断当前的对话状态时就有了两种选择,这两种选择分别对应了两种不同的处理方式,一种是1-Best方式,另一种则是N-Best方式

  • 1-Best方式指DST判断当前对话状态时只考虑置信程度最高的情况,因此维护对话状态的表示时,只需要等同于槽位数量的空间,如图:

    1-Best

  • N-Best方式指DST判断当前对话状态时会综合考虑所有槽位的所有置信程度,因此每一个槽位的N-Best结果都需要考虑和维护,并且最终还需要维护一个槽位组合在一起(overall)的整体之心程度,将其作为最终的对话状态判断依据,如图:

    N-Best

3. 举例说明

整个问答类似这样:

- 用户:我要查天气
- 系统:好的,要查哪个城市的?
- 用户:广州
- 系统:查哪天的?
- 用户:明天
- 系统:明天广州天气是xxx

在这个过程中我们可以认为有三轮交互(用户到系统),如果写成用户行为和系统行为那么是这样的:

- User:requestWeather()
- Sys:request(city)
- User:inform(city=广州)
- Sys:request(date)
- User:inform(date=明天)
- Sys:informWeather(city=广州,date=明天)

我们分为3轮,看看语义帧的变化:

- User:requestWeather()
- State Before DST:
  - user_action_t = null
  - sys_action_t-1 = null
  - city = null
  - date = null
- State After DST:
  - user_action_t = requestWeather()
  - sys_action_t-1 = null
  - city = null
  - date = null
- Sys:request(city)

在通过DST之前,我们可以认为系统是一篇空白,都是null(空)。

在DST之后我们可以认为DST更新了user_action, 而用户行为也可以认为是自动更新的而不是DST的功劳。

- User:inform(city=广州)
- State Before DST:
  - user_action_t = requestWeather()
  - sys_action_t-1 = request(city)
  - city = null
  - date = null
- State After DST:
  - user_action_t = inform(city=广州)
  - sys_action_t-1 = request(city)
  - city = 广州
  - date = null
- Sys:request(date)

在通过DST之前,State和前一轮通过DST之后是一致的。

然后因为提供了“北京”这个信息,所以DST更新了city这个项。所以本质上DST在这里的作用只有一个,决定某个语义帧的一项,要不要更新。

我们例子比较简单,但是DST是很多不能更新的时候,例如用户输入的系统没有理解,或者理解的概率很低,那么DST就不应该被更新。当然此时应该有其他的语义帧来标识这种状态。

- User:inform(date=明天)
- State Before DST:
  - user_action_t = inform(city=广州)
  - sys_action_t-1 = request(date)
  - city = 广州
  - date = null
- State After DST:
  - user_action_t = inform(date=明天)
  - sys_action_t-1 = request(date)
  - city = 广州
  - date = 明天
- Sys:informWeather(city=北京,date=明天)

这一轮的对话基本同上。


我们假设一种情况,用户会说错,或者是一些重要选项其实可能是需要用户确认的情况。例如用户如果买票,但是要么语音识别出错,要么NLU出错,把“北京市”识别成“北海市”(广西的一个市),例如用户说:“我想去北京看北海,请问天气怎么样”,但是错误的被NLU理解成了想去北海市,或者NLU同时识别了北京市和北海市,或者这两者的置信度都比较低(NLU不确定用户想要什么),那么就应该做出一个让用户确认的操作,一般这个操作被称为confirm。我们看一下假设对话行为和状态包含了confirm会如何。

{
    "city": null,
    "city_confirmed": false,
    "date": null,
    "date_confirmed": false
}
- 用户:我要查天气
- 系统:好的,要查哪个城市的?
- 用户:我想去北京看北海
- 系统:请问是北京市吗?
- 用户:是的
- 系统:那么查哪天的?
- 用户:明天
- 系统:明天北京的天气是xxx

在这个过程中我们可以认为有三轮交互(用户到系统),如果写成用户行为和系统行为那么是这样的:

- User:requestWeather()
- Sys:request(city)
- User:inform(city=北京), inform(city=北海) # 这两个行为是并列的,但是置信度不同
- Sys:confirm(city=北京)
- User:confirm
- Sys:request(date)
- User:inform(date=明天)
- Sys:informWeather(city=北京, date=明天)

主要变化的是下面这一步:

- User:inform(city=北京), inform(city=北海) # 这两个行为是并列的,但是置信度不同
- Sys:confirm(city=北京)
- User:confirm

我们可以认为逻辑是这样的:假设NLU输出的结果,出现了太多不确定的内容,或者不确定性大于某个阈值,系统就可以反问用户来确认答案。

当然从系统设计上来说,系统反问次数越多,系统状态正确的可能性越大,但是系统反问越多,系统的可用性就越低,因为太长的对话本身会导致用户体验的下降

4. 是否可以无状态?

首先答案,基本上是可以的。

实际上在一些研究上的End-to-End系统中,DST本身只是以一种向量分布或者类似完全记忆化的形式存储在神经网络中,而不需要直接的定义。这不能算无状态,但是也可以算隐藏状态

但是缺点也是显而易见的,因为并不知道状态,至少不知道状态的实际意义(因为它可能只是一个分布),所以假设它输入DPL导致错误的系统行为,我们也很难调试

5. 实现方式

实现DST模块的方法主要有:基于条件随机场模型的序列跟踪模型、基于RNN和LSTM的序列跟踪模型等。

参考文献

  1. 自然语言处理与实践

你可能感兴趣的:(聊天机器人-DST模块)