多轮对话模型漫游
1. 基于问答对的模型的缺陷
基于问答对的chatbot我们已经讨论过,不过,这种简单的模型用来玩玩是可以的,真要是更强的场景是不适用的,因为实际场景中,我们的对话轮次是多轮的,比如下面的对话场景:
- “哪家餐馆好吃?”
- “你想吃什么口味呢?”
- “火锅吧!”
- “好的,为你挑选一下火锅店…”
………
上面的场景需要多次的对话才能确定意图,其意图的确定也是有依赖关系的,而简单的问答对则显然不会捕捉到这种依赖关系,所以其表达能力也弱。
现在,是时候进入更高层次的chatbot中了!
2. 多轮对话
多轮对话聊天机器人,作为人工智能的典型应用场景,也是一项极具挑战的任务,不仅涉及多方面异构知识的表示、抽取、推理和应用(如语言知识、领域知识、常识知识等),还涉及包括自然语言理解在内的其他人工智能核心技术(如用户画像、对话管理等)的综合利用,所以还是比较复杂的。
2.1 HERD
该模型提出于2015年,基于seq2seq而有超脱于seq2seq。
回忆一下普通的seq2seq的特点,
对于这种encoder-decoder结构,由于其encoder的串行特点,要用来建模多轮对话的话,只能通过将句子依次输入,并用前个句子的final state作为下个句子的init state来实现多轮会话的处理,通过这种方式,将句子信息不断传递下去,这样的话 context vector 里的信息会在 propagation 的过程中会被新句子的词语逐步稀释,对信息/梯度的传播极不友好;或者采用更好一点的做法,将每个句子的final state进行拼接或者某种非线性处理,依次实现信息的揉合。然而,不论怎么样,都没有很好的捕获到句子间的时序关系。
这时候,看到句子间的时序关系,你看到了什么?没错,就是关键的“时序”两个字,我们该想到,既然句子是短语的时序组合,而会话是句子的时序组合,那么如果可以用RNN建模句子,那么同样用RNN建模会话也是自然而然的吧?基于这种想法,HERD应运而生。
上图便是HERD的流程,相比于只有单个encoder-decoder的seq2seq结构,该模型引入了一个被称为context encoder的中间层,通过该层实现对句子级的建模,具体做法是:
-
encoder RNN
该encoder就是seq2seq中的encoder,用来对单个句子进行建模,其建模出的向量被称为utterance vector,它也是该encoder的 last hidden state,这个encoder记忆的是句子的细节。
-
context RNN
n个句子的会话级建模,其输入是n个句子的utterance vector,通过该RNN实现了对句子间时序的建模。该RNN的hidden会作为decoder的init hidden
-
decoder RNN
通过接受context RNN的hidden来对当前句子进行建模,这时候的hidden不仅包括了句子级的信息,也包括了会话的时序信息。
应该说该模型的想法还是挺不错的,它能够丰富编码信息,使得编码信息不仅仅包含当前输入句子的语义信息,还包含了历史输入句子的语义信息,这种丰富的上下文编码信息对解码器来说更能准确地解码出相应的response。不过 实际来看,效果并没有超过传统的seq2seq多少。
2.2 VHRED
RNNLM和HERD,以及其他基于RNN结构的模型,在生成有意义的会话方面存在比较严重的问题,VHERD(variable hierarchical recurrent encoder-decoder)作者认为这个问题的根源在于对于输出分布的参数化过程,因为这个过程会生成过程施加一个强约束:唯一的变化来源是通过条件输出分布来建模(the only source of variation is modelled through the conditional output distribution.),而这从两个方面来说是不利的:
- 从概率的角度来说,由于随机噪音只在低层注入,模型会倾向于捕捉序列中的局部结构,而不是全局或者长期(long-term)结构,这是因为在低层注入的随机变化受到强烈的约束,使其与之前的观测值一致,但只受到弱的约束,使其与较早的观测值或未来的观测值一致。
- 从计算学习的角度来说,RNN的state $h_{m}$必须总结到时间步m的所有信息以便产生下一个输出(短期目标),同时需要在嵌入空间中占据一个维持现实输出轨迹的位置,以便生成可能的未来token(长期目标).而由于梯度消失的影响,模型会更倾向于维持短期目标。
注解:作者这里写的十分理论,我的理解是模型产生的输出是通过decoder来建模的,而decoder在输出第一个token以后,其后续的输出都是已经确定的了(因为参数已经固化了),那么增加多样性的一个方法就是噪音来扩大input在语义空间的范围,比如针对一个句子“你好啊”,如果不加噪音,那么通过encoder编码以后,如果直接decoder,这时该句子便对应了语义空间的一个点,添加噪音以后,由于强迫decoder从一定波动范围内还原encoder,所以这时候该句子便被编码为了一段范围,这样的话就大大缓解了数据的稀疏性,有利于生成多样性的句子。不过这个噪音如果添加的比较低层,比如word层面,那么由于模型本身被强约束于与输出word一致,模型便会倾向于捕捉局部结构,对于整体结构的约束便不是那么强。
从上面的讨论中,作者提出了一种新的结构,叫做Latent Variable Hierarchical Recurrent Encoder-Decoder (VHRED),VHRED 针对上面的问题引入了全局(语义层面)的随机因素,一是能增强模型的 robustness,二是能捕捉 high level concepts。Latent variable 使得 response 不再和一个/几个固定的句子绑定,鼓励了回复的多样性。它的主要流程包括两步:
-
随机采样latent variables
这是论文的核心,其思想来自于VAE。在VAE领域,其目标专注于通过隐变量来生成目标数据,其最初应用于图像领域,可以通过采样的方式生成多种多样的图像。
VAE的推导略微复杂,不过其思想却十分精妙,大概可以分为以下的步骤来理解:
- 传统的auto-encoder直接对样本x进行编码得到latent vector z,这种方式会将x编码到latent space上的一个离散点上,即便加上噪音也只是扩大了x在latent space上的一段范围,无法覆盖整个latent space,这样的问题就是样本在latent space上分布的稀疏性,碰到一些未被覆盖到的空白编码点就会出现乱码的现象,而且这种现象概率还比较高(例子)。
- VAE另辟蹊径,将latent vector z编码成了一种我们常见的分布,比如标准正态分布,然后用z去构建x,这样带来的好处就是针对每一个x,其在latent space中的分布就是占据了整个空间,只是在原编码处出现的概率最高,这样的话,针对未出现在训练集中的样本x1,,其解码结果就是训练集中的样本的叠加和,与x1分布类似的样本会占据更大的权重,而与其分布差异越大的样本占的权重也越小,越起不到作用,这样叠加的结果就会导致良好的多样性。
- VAE使我们能够为自然语言建模训练一个latent vector模型,这赋予我们几个优势。首先,latent vector可以学习一个可解释的整体表征,如话题、语调或高级语法属性。其次,latent vector可以通过编码自然语言的全局和长期结构来模拟自然语言固有的丰富变异性,这是浅层的生成过程(如vanilla RNNs)很难捕捉到的,
-
生成输出序列
注意上图的prior parameterization和posterior parameterization,在VHRED模型中,在测试时,不存在当前时间步的sentence,使用prior parameterization来采样z;在训练的时候,需要聚合更多的信息来辅助训练,使用posterior parameterization来采样z。 \(test: P_{\theta }(z_{n}|w_{1},...,w_{n-1})=N(\mu_{prior}(w_{1},...,w_{n-1}),\sum_{prior}(w_{1},...,w_{n-1}))\)
\[train: P_{\theta }(z_{n}|w_{1},...,w_{n})=N(\mu_{posterior}(w_{1},...,w_{n}),\sum_{posterior}(w_{1},...,w_{n}))\] 通过上面的采样以后,将其与context RNN的输出进行cat来生成decoder的initial hidden来解码,后续的过程就是普通的RNN操作了。
公式流程为: \(h_{t-1}^{enc} = f_{\theta}^{enc}(x_{t-1})\)
\[h_{t}^{cxt} = f_{\theta}^{cxt}(h_{t-1}^{cxt},h_{t-1}^{enc})\] \[p_{\theta}(z_{t}^{utt}|x_{<t})=N(z|\mu_{t},\sigma_{t}I)\] \[where:\ \mu_{t}=MLP_{\theta}(h_{t}^{cxt})\] \[\sigma_{t}=Softplus(MLP_{\theta}(h_{t}^{cxt}))\] \[p_{\theta}(x_{t}|x_{<t})=f_{\theta}^{dec}(x|h_{t}^{cxt},z_{t}^{utt})\] 其中,$z_{t}^{utt}$的posterior可以用如下公式求出: \(q_{\phi}(z_{t}^{utt}|x_{\leq t}) = N(z|u_{t}^{'},\sigma_{t}^{'}I)\)
\[where: \mu_{t}^{'} = MLP_{\phi}(x_{t},h_{t}^{cxt})\] \[\sigma_{t}^{'}=Softplus(MLP_{\phi}(x_{t},h_{t}^{cxt}))\]从HRED到VHRED,它们的想法是美好的,不过从模型本身来看,个人感觉是不怎么样的,它们更像是基于纯研究的背景,而非基于落地的背景,因为context RNN在实际应用中要面临很多问题,比如如果确定一轮会话的终结,会话过长建模等等,所以个人而言,这些方法的实用价值并不大。
2.3 VHCR
VHCR(Variational Hierarchical Conversation RNN)是提出于2018年的一篇论文,论文作者肯定了VAE在会话生成过程中的重要性,不过其也提出了VAE存在的缺陷:
-
VAE degeneration
VAE优化的目标函数为: \(log p_{\theta }(x) \geqslant L(\theta ,\phi ;x)\\=E_{q_{\phi }(z|x)}[-log q_{\phi}(z|x) + log p_{\theta }(x,z)]\\=-D_{KL}(q_{\phi}(z|x)||p(z))+E_{q_{\phi}(z|x)}[log p_{\theta}(x|z]\)
其中,各公式含义如下:
公式右侧第一项为KL散度项,如果缺失这一项,VAE就退化为普通的AE,而在优化过程中,一个容易出现的现象就是KL散度会被优化为0,这时候只有第二项的重构项,这种现象就是KL散度消失(KL Vanishing)。发生这种现象的时候,VAE的latent space中,就不能有效接受来自encoder的信息,因为这时候有: \(q_{\phi}(z|x)\simeq p(z)= N(\mu,\sigma)\) 这时候的$\mu$与$\sigma$基本上与x脱钩,所以z便被忽略掉了。
从公式8可以看出,context RNN也可以看作是一个高层的decoder,他和decoder RNN一起构建了一个层级的RNN decoders,层级 RNN decoders 足够强大,以至于它只使用编码输出分布来不使用latent variables来建模数据,虽然可以通过一些heuristics比如KL 退火(annealing)或者word drop regularization来缓解,不过无法完全解决。
-
数据稀疏性
conditional VAE结构中,一个语句的生成是以context为条件的,即之前的语句序列,这会诱发严重的数据稀疏性。即使是大规模的训练语料,当以context为条件时,也只存在很少的目标语句。因此,分层RNNs可以很容易地记忆上下文与语句之间的关系,而不依赖于latent variables,这时候,即便加上word drop regularization,不过它惩罚的只是decoder,context RNN本身也足够预测下一个utterance。
针对上面的问题,作者提出了自己的解决方案:
-
引入了一个全局对话latent variable以及局部语句latent variable来建立一个层次化的潜变量结构
针对一个会话$c=(x_{1},x_{2},…,x_{n})$,引入全局会话latent variable $z^{conv}$负责生成该会话的utterances序列:\(p_{\theta}(c|z^{conv}) = p_{\theta}(x_{1},...,x_{n}|z^{conv})\) 其计算流程为: \(h_{t}^{enc}=f_{\theta}^{enc}(x_{t})\)
\[h_{t}^{cxt} = \begin{cases} & MLP_{\theta}(z^{conv}) \quad {if}\ t=0 \\ & f_{\theta}^{cxt}(h_{t-1}^{cxt},h_{t-1}^{enc},z^{conv}) \quad otherwise \end{cases}\] \[p_{\theta}(x_{t}|x_{<t},z^{utt},z^{conv})=f_{\theta}^{dec}(x|h_{t}^{cxt},z_{t}^{utt},z^{conv})\] \[p_{\theta}(z^{conv})=N(z|0,I)\] \[p_{\theta}(z_{t}^{utt}|x_{<t},z^{conv})=N(z|\mu_{t},\sigma_{t}I)\] \[where: \mu_{t}=MLP_{\theta}(h_{t}^{cxt},z^{conv})\] \[\sigma_{t}=Softlus(MLP_{\theta}(h_{t}^{cxt},z^{conv}))\] 对于$z^{conv}$,使用双向RNN,标记为$f^{conv}$,其生成过程如下: \(q_{\phi}(z^{conv}|x_{1},...,x_{n})=N(z|\mu^{conv},\sigma^{conv}I)\)
\[where: h^{conv}=f^{conv}(h_{1}^{enc},...h_{n}^{enc})\] \[\mu^{conv}=MLP_{\phi}(h^{conv})\] \[\sigma^{conv}=Softplus(MLP_{\phi}(h^{conv}))\] 对于局部变量$z_{t}^{utt}$的posteriors,其建立在$z^{conv}$基础上: \(q_{\phi}(z_{t}^{utt}|x_{1},...,x_{n},z^{conv})=N(z|\mu_{t}^{'},\sigma_{t}^{'}I)\)
\[where:\mu_{t}^{'}=MLP_{\phi}(x_{t},h_{t}^{cxt},z^{conv})\] \[\sigma_{t}^{'}=Softplus(MLP_{\phi}(x_{t},h_{t}^{cxt},z^{conv}))\]通过对比可知,此时的context RNN以及decoder RNN生成都需要建立在$z^{conv}$的基础上,而$z^{conv}$则综合建模了所有的utterances,其起到的作用就是全局信息,再加上$z_{t}^{utt}$本身的分布,这就构成了一种层次的latent space,这便强迫模型去利用latent space信息,又由于$z^{conv}$不依赖于条件结构,即最终的输出不是仅仅建立在它上面的,所以它也不会产生数据稀疏问题。
-
utterance drop regularization
虽然建立了层次的latent space,不过作者在实践中发现,层次RNN decoders仍然非常的expressive,导致模型仍然倾向于忽略$z^{conv}$和$z_{t}^{utt}$,所以作者又提出了utterance drop regularization来缓解,具体做法就是在每个时间步,$h_{t}^{enc}$以概率p被替换为一个设定的未知向量$h^{unk}$,这个做法降低了层次RNN decoders的自适应表达能力,同时又因为为$h_{t}^{cxt}$引入了噪音,所以也缓解了数据稀疏性问题,
3. 总结
作为新时代的”老物”-RNN,尽管还有一些工作是基于其开展的,不过,现在其越来越边缘化了,在nlp领域引领风骚的是各种transformer的变体,比如大火的BERT,GPT,T5等等,这些最新的模型把nlp推到了一个非常高的程度,后续会记录一些这类模型的细节。