TripPy: A Triple Copy Strategy for Value Independent Neural Dialog State Tracking
模型的计算方法与 DS-DST 几乎完全一致,只不过补充了几点 slot gate 的类型。
唯一不同的在特征提取这块。DS-DST 将 CLS
,一个域槽对和对话上下文拼接起来。由于所有域槽对的词向量是不同的,则可以凭此遍历所有的域槽对,使得每次捕获到的特征都是根据域槽对的变化而变化。所以当使用 CLS
进行 slot gate 分类时,可以确定该 slot gate 是基于某一域槽对的,并且 CLS
表征总是不同的。但是这样的做法计算起来特别麻烦,因为如果想要向量化,必须复制 N 份上下文(N 为域槽对数量) 。
TripPy 应该是略微地改进了它,它移除了输入中的域槽对,其他基本不变,顶多是改变了一下上下文的输入顺序,这并无大碍。然后,TripPy 为每一个域槽对都设计了一个线性层用于计算 slot gate。这也能使得 CLS
的表征总是不同,因为线性层中的权重矩阵是不同的。但是这样貌似更加无法向量化了? 反而,弄巧成拙?
所以,我认为在 dst 模型上,还是 TRADE 模型设计的更合理,它是采用了 attention 的机制。相比于 DS-DST 和 TripPy,参数量大大地减少,并且可以向量化。
图 TripPy
本文提出三种复制机制:1) 从用户语句中直接提取出槽值的跨度预测(span prediction);2) 从 system inform memory 中复制出槽值,其为系统回复操作的追踪;3) 从对话状态历史中复制槽值。
令 \(X = \{(U_1, M_1), \cdots, (U_T, M_T)\}\) ,\(U_t\) 是 \(t\) 轮时的用户语句,\(M_t\) 是 \(t\) 轮时的回复语句。模型的任务是 1) 决定每轮是否提到了 \(S = \{S_1, \cdots, S_N\}\) 中的 \(N\) 个域槽对;2) 预测每个 \(S_n\) 的槽值;3) 追踪整场对话过程中的对话状态。
Context Encoder
使用 BERT 提取上下文特征,公式为:
\(R_t = BERT([CLS] \oplus U_t \oplus [SEP] \oplus M_t \oplus [SEP] \oplus H_t \oplus [SEP])\)
其中 \(U_t\) 是 t 轮的用户语句,\([CLS], [SEP]\) 都是 BERT 需要的特殊符号,分别为分类特殊符和分隔符,\(H_t\) 为对话历史。那么 \(R_t = [r^{CLS}_t, r^1_t, \cdots, r^{seq_{max}}_t]\) 。以上都是比较基础的公式,具体说明略。值得注意得是 ,TripPy 的输入是逆序的,先输入 t 轮的对话,再输入逆序的历史对话。
Slot Gates
Slot Gate 的思想应该取自 TRADE 模型,简单来说,就是设计一个多分类器,判断接下来的操作应该交给哪个组件执行,一般来说可以选择 \(\{None, dontcare, Ptr, \cdots\}\) 。
TripPy 为每一个域槽对都配备了一个 slot gate。 还是跟以往的做法差不多,将槽值的识别问题转换为一个分类问题。与 TRADE 模型不同的是,TripPy 的 slot gate 在每轮为槽位 \(S_n\) 进行分类,类别包括 \(C = \{none, dontcare, span, inform, refer\}\) ,其中 inform 代表系统的通知,refer 代表历史对话状态中所提到的,其他都是类似的,就不提了。
由于在 Context Encoder 中已经提取到了上下文特征,而且这步也是 BERT 做的,所以 Slot Gate 实际上就是做几个线性分类而已。BERT 可以得到 \(r^{CLS}_t\) ,这代表 \(t\) 轮 \([CLS]\) 的表征,那么域槽对 \(S_n\) 在类别 \(C\) 上的概率分布为:
\[p^{gate}_{t,s}(r^{CLS}_t) = softmax(W^{gate}_s \cdot r^{CLS}_t + b^{gate}_s) \in \mathbb{R}^5
\]
需要注意的是,上述的分类器是对一个域槽对进行五元分类。但是在系统中我们拥有 \(N\) 个域槽对,所以我们需要 \(N\) 个上述的分类器 ,这就导致需要一定的参数量。
对于特殊的槽位 Boolean Slot ,也使用了类似的方法,但是类别 \(C_{bool} = \{none, dontcare, true, false\}\) 。
Span-based Value Prediction
使用 Ptr 神经网络预测槽值在用户语句中的位置,包括 start 以及 end 位置。如果 end > start,则简单地将跨度(span)置为空。
系统通知记忆(System Inform Memory) \(I_t = \{I^1_t, \cdots, I^N_t\}\) 追踪系统提到的所有槽值对。简单来说,这就是一个 python 中的 dict,记录每一个槽值对是否被系统提及到。简单来说,如果用户提到了某个系统所通知给用户的槽值,那么槽位应该直接填充这个“通知值”,而不是去使用 Ptr 预测跨度,然后从用户的语句中提取出来。
这听起来可能有点奇怪,因为如果用户提到了某个系统的“通知值”,那么理所当然地我们也可以使用 Ptr 从用于语句中提取出来,为什么要多此一举使用 System Inform Memory 呢?原因在于,用户所提到的“通知值”可能并不是其本身。思考下面的例句,“系统:‘xx酒店有你想要的食物类型。’;用户:‘好的,就是它 了。’”。可见系统提到的“xx酒店”,用户并没有直接引用它,而是使用了一种共指 的语法。
DS Memory for Coreference Resolution
更复杂的对话需要进行共指解析。简单来说,就是某一个槽位的槽值与另一个槽位的槽值相同,所以使用一个 N 元(槽位的数量)分类器,用于计算当前槽位的槽值是否指向另外一个槽位。事实上,这与 System Inform Memory for Value Prediction 类似,都是解决共指解析 。
Auxiliary Features
辅助特征。个人认为这种特征没什么特别大的意义。
Dialog State Update
使用与 Chao and Lane (2019) 同样的规则更新机制。每轮,如果槽值不为 none ,则更新槽值;否则不更新。
总结
这模型与 TRADE 之类的 span-based 或者 open-vocabulary 模型有点不同。这个模型需要为每个域槽对都设计一个分类器,即有 \(N\) 个权重矩阵,然后将 \([CLS]\) 的特征输入每个分类器 从而判断该域槽对是否被用户提取。而 TRADE 的做法是捕获域槽对的隐藏状态,然后将该隐藏状态 \(h^{dec}\) 输入一个分类器 从而判断该域槽对是否被用户提取。粗体就是区别,一个输入的是 \([CLS]\) 特征,其是固定的,只能使用不同的权重矩阵来判断不同的域槽对,另一个输入的是域槽对的隐藏状态 \(h^{dec}\) ,由于域槽对是不同的,则 \(h^{dec}\) 也是不同的,所只需要一个分类器即可。