“你什么意思”之基于RNN的语义槽填充(Pytorch实现)

1. 概况

1.1 任务

口语理解(Spoken Language Understanding, SLU)作为语音识别与自然语言处理之间的一个新兴领域,其目的是为了让计算机从用户的讲话中理解他们的意图。SLU是口语对话系统(Spoken Dialog Systems)的一个非常关键的环节。下图展示了口语对话系统的主要流程。

SLU主要通过如下三个子任务来理解用户的语言:

  1. 领域识别(Domain Detection)
  2. 用户意图检测(User Intent Determination)
  3. 语义槽填充(Semantic Slot Filling)

例如,用户输入“播放周杰伦的稻香”,首先通过领域识别模块识别为"music"领域,再通过用户意图检测模块识别出用户意图为"play_music"(而不是"find_lyrics" ),最后通过槽填充对将每个词填充到对应的槽中:"播放[O] / 周杰伦[B-singer] / 的[O] / 稻香[B-song]"。

从上述例子可以看出,通常把领域识别和用户意图检测当做文本分类问题,而把槽填充当做序列标注(Sequence Tagging)问题,也就是把连续序列中每个词赋予相应的语义类别标签。本次实验的任务就是基于ATIS 数据集进行语义槽填充。(完整代码地址https://github.com/llhthinker/slot-filling)

1.2 数据集

本次实验基于ATIS(Airline Travel Information Systems )数据集。顾名思义,ATIS数据集的领域为"Airline Travel"。ATIS数据集采取流行的"in/out/begin(IOB)标注法": "I-xxx"表示该词属于槽xxx,但不是槽xxx中第一个词;"O"表示该词不属于任何语义槽;"B-xxx"表示该词属于槽xxx,并且位于槽xxx的首位。部分ATIS训练数据集如下:

what    O
is  O
the O
arrival B-flight_time
time    I-flight_time
in  O
san B-fromloc.city_name
francisco   I-fromloc.city_name
for O
the O
DIGITDIGITDIGIT B-depart_time.time
am  I-depart_time.time
flight  O
leaving O
washington  B-fromloc.city_name

ATIS数据集一共有83种语义槽,因此序列标注的标签类别一共有\(83+83+1=167\)个。ATIS数据集分为训练集和测试集,数据规模如下表:

训练集 测试集
句子总数 4978个 893个
词语总数 56590个 9198个
句子平均词数 11.4个 10.3个

2. 模型

上文中提到,通常把槽填充当做序列标注问题。很多机器学习算法都能够解决序列标注问题,包括HMM/CFG,hidden vector state(HVS)等生成式模型,以及CRF, SVM等判别式模型。本次实验主要参考论文《Using Recurrent Neural Networks for Slot Filling in Spoken Language Understanding 》 ,基于RNN来实现语义槽填充。

RNN可以分为简单RNN(Simple RNN)和门控机制RNN(Gated RNN),前者的RNN单元完全接收上个时刻的输入;后者基于门控机制,通过学习到的参数自行决定上个时刻的输入量和当前状态的保留量。下面将介绍Elman-RNN, Jordan-RNN, Hybrid-RNN(Elman和Jordan结合)这三种简单RNN,以及经典的门控机制RNN:LSTM。

2.1 Elman-RNN

Elman-RNN将当前时刻的输入\(x_t\)和上个时刻的隐状态输出\(h_{(t-1)}\)作为输入,具体如下:

\[\begin{split}\begin{array}{ll}h_t = \sigma(W_{ih} x_t + b_{ih} + W_{hh} h_{(t-1)} + b_{hh}) \end{array}\end{split}\]

需要说明的是,Pytorch默认的RNN即为Elman-RNN,但是它只支持\(\tanh\)和ReLU两种激活函数。本次实验按照论文设置,激活函数均采取sigmoid函数,使用Pytorch具体实现如下:

class ElmanRNNCell(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(ElmanRNNCell, self).__init__()
        self.hidden_size = hidden_size
        self.i2h_fc1 = nn.Linear(input_size, hidden_size)
        self.i2h_fc2 = nn.Linear(hidden_size, hidden_size)
        self.h2o_fc = nn.Linear(hidden_size, hidden_size)

    def forward(self, input, hidden):
        hidden = F.sigmoid(self.i2h_fc1(input) + self.i2h_fc2(hidden))
        output = F.sigmoid(self.h2o_fc(hidden))
        return output, hidden

2.2 Jordan-RNN

Jordan-RNN将当前时刻的输入\(x_t\)和上个时刻的输出层输出\(y_{(t-1)}\)作为输入,具体如下:

\[\begin{split}\begin{array}{ll}h_t = \sigma(W_{ih} x_t + b_{ih} + W_{yh} y_{(t-1)} + b_{yh}) \end{array}\end{split}\]

使用Pytorch具体实现如下,其中\(y_0\)初始化为可训练的参数:

class JordanRNNCell(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(JordanRNNCell, self).__init__()
        self.hidden_size = hidden_size
        self.i2h_fc1 = nn.Linear(input_size, hidden_size)
        self.i2h_fc2 = nn.Linear(hidden_size, hidden_size)
        self.h2o_fc = nn.Linear(hidden_size, hidden_size)
        self.y_0 = nn.Parameter(nn.init.xavier_uniform(torch.Tensor(1, hidden_size)), requires_grad=True)

    def forward(self, input, hidden=None):
        if hidden is None:
            hidden = self.y_0
        hidden = F.sigmoid(self.i2h_fc1(input) + self.i2h_fc2(hidden))
        output = F.sigmoid(self.h2o_fc(hidden))
        return output, output

2.4 Hybrid-RNN

Hybrid-RNN将当前时刻的输入\(x_t?\),上个时刻的隐状态\(h_{(t-1)}?\) 以及上个时刻输出层输出\(y_{(t-1)}?\)作为输入,具体如下:

\[\begin{split}\begin{array}{ll}h_t = \sigma(W_{ih} x_t + b_{ih} + W_{hh} h_{(t-1)} + b_{hh} + W_{yh} y_{(t-1)} + b_{yh}) \end{array}\end{split}\] ,并且\(y_0\)初始化为可训练的参数。使用Pytorch具体实现如下:

class HybridRNNCell(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(HybridRNNCell, self).__init__()
        self.hidden_size = hidden_size
        self.i2h_fc1 = nn.Linear(input_size, hidden_size)
        self.i2h_fc2 = nn.Linear(hidden_size, hidden_size)
        self.i2h_fc3 = nn.Linear(hidden_size, hidden_size)
        self.h2o_fc = nn.Linear(hidden_size, hidden_size)
        self.y_0 = nn.Parameter(nn.init.xavier_uniform(torch.Tensor(1, hidden_size)), requires_grad=True)

    def forward(self, input, hidden, output=None):
        if output is None:
            output = self.y_0
        hidden = F.sigmoid(self.i2h_fc1(input)+self.i2h_fc2(hidden)+self.i2h_fc3(output))
        output = F.sigmoid(self.h2o_fc(hidden))
        return output, hidden

2.5 LSTM

LSTM引入了记忆单元\(c_t\)和3种控制门,包括输入门(input gate)\(i_t\),遗忘门(forget gate)\(f_t\),输出门(output gate)\(o_t\), 首先,输入层接受当前时刻输入\(x_t\)和上个时刻隐状态输出\(h_{(t-1)}\),通过\(\tanh\)激活函数得到记忆单元的输入\(g_t\); 然后遗忘门\(f_t\)决定上个时刻记忆单元\(c_{(t-1)}\)的保留比例,输入门\(i_t\)决定当前时刻记忆单元的输入\(g_t\)的保留比例,两者相加得到当前的记忆单元\(c_t\); 最后记忆单元\(c_t\)通过\(\tanh\)激活函数得到的值在输出门\(o_t\)的控制下得到最终的当前时刻隐状态\(h_t\), 具体如下:

\[\begin{split}\begin{array}{ll}i_t = \sigma(W_{ii} x_t + b_{ii} + W_{hi} h_{(t-1)} + b_{hi}) \\f_t = \sigma(W_{if} x_t + b_{if} + W_{hf} h_{(t-1)} + b_{hf}) \\g_t = \tanh(W_{ig} x_t + b_{ig} + W_{hg} h_{(t-1)} + b_{hg}) \\o_t = \sigma(W_{io} x_t + b_{io} + W_{ho} h_{(t-1)} + b_{ho}) \\c_t = f_t c_{(t-1)} + i_t g_t \\h_t = o_t \tanh(c_t)\end{array}\end{split}\]

Pytorch已经实现了LSTM, 只需要调用相应的API即可,调用的代码片段如下:

self.rnn = nn.LSTM(input_size=embedding_dim,
                   hidden_size=hidden_size,
                   bidirectional=bidirectional,
                   batch_first=True)

3. 实验

3.1 实验设置

实验基于Python 3.6Pytorch 0.4.0,为进行对照实验,下列设置针对所有RNN模型:

  • 所有RNN模型均只使用单层;
  • 词向量维度设置为100维,并且随机初始化,在训练过程中进行调整;
  • 隐状态维度设置为75维;
  • 采用带动量的随机梯度下降(SGD),batch size为1,学习率(learning rate)为0.1,动量(momentum)为0.9并保持不变;
  • epoch=10;
  • 每种RNN模型都实现单向(Single)和双向(Bi-Directional),并分别训练。

3.2 实验结果

在使用CPU的情况下,不同模型在测试集的\(F_1\)得分以及平均一个epoch训练时长的结果如下:

\(F_1(\%) / T(s)\) Elman Jordan Hybrid LSTM
Single 87.26 / 438 87.90 / 487 88.46 / 494 92.16 / 3721
Bi-Directional 92.88 / 565 90.31 / 580 91.85 / 613 93.75 / 4357

从上表中可以看出:

  • 基于门控机制的LSTM由于其参数和运算步骤的增加,一个epoch的训练时长是另外三种Simple RNN的9倍左右,而\(F_1\)得分也比Simple RNN高;
  • 双向(Bi-Directional)RNN的\(F_1\)得分普遍比单向(Single)RNN高,而运行时间也多一些。

在使用同一块GPU的情况下,不同模型在测试集的\(F_1\)得分以及平均一个epoch训练时长的结果如下:

\(F_1(\%) / T(s)\) Elman Jordan Hybrid LSTM
Single 88.89 / 35.2 88.36 / 41.3 89.65 / 43.5 92.44 / 16.8
Bi-Directional 91.78 / 68.0 89.82 / 72.2 93.61 / 81.6 94.26 / 18.7

从上表中可以看出,即使是随机梯度下降(batch_size=1),GPU的加速效果仍然相当明显。值得指出的是,虽然LSTM的运算步骤比其他三种Simple-RNN多,但是用时却是最少的,这可能是由于LSTM是直接调用Pytorch的API,针对GPU有优化,而另外三种的都是自己实现的,GPU加速效果没有Pytorch好。

4. 总结与展望

总的来说,将槽填充问题当做序列标注问题是一种有效的做法,而RNN能够较好的对序列进行建模,提取相关的上下文特征。双向RNN的表现优于单向RNN,而LSTM的表现优于Simple RNN。对于Simple RNN而言,Elman的表现不比Jordan差(甚至更好),而用时更少并且实现更简单,这可能是主流深度学习框架(TensorFlow / Pytorch等)的simple RNN是基于Elman的原因。而Hybrid作为Elman和Jordan的混合体,其训练时间都多余Elman和Jordan,\(F_1\)得分略有提升,但不是特别明显(使用CPU时的双向Elman表现比双向Hybrid好),需要更多实验进行验证。

从实验设置可以看出,本次实验没有过多的调参。如果想取得更好的结果,可以进行更细致的调参,包括 :

  • 改变词向量维度和隐状态维度;
  • 考虑采用预训练词向量,然后固定或者进行微调;
  • 采用正则化技术,包括L1/L2, Dropout, Batch Normalization, Layer Normalization等;
  • 尝试使用不同的优化器(如Adam),使用mini-batch,调整学习率;
  • 增加epoch次数。

此外,可以考虑在输入时融入词性标注和命名实体识别等信息,在输出时使用Viterbi算法进行解码,也可以尝试不同形式的门控RNN(如GRU,LSTM变体等)以及采用多层RNN,并考虑是否使用残差连接等。

参考资料

Mesnil G, Dauphin Y, Yao K, et al. Using recurrent neural networks for slot filling in spoken language understanding[J]. IEEE/ACM Transactions on Audio, Speech, and Language Processing, 2015, 23(3): 530-539.

Wikipedia. Recurrent neural network. https://en.wikipedia.org/wiki/Recurrent_neural_network

PyTorch documentation. Recurrent layers. http://pytorch.org/docs/stable/nn.html#recurrent-layers

Hung-yi Lee. Machine Learning (2017,Spring). http://speech.ee.ntu.edu.tw/~tlkagk/courses/ML_2017/Lecture/RNN.pdf

YUN-NUNG (VIVIAN) CHEN. Spring 105 - Intelligent Conversational Bot. https://www.csie.ntu.edu.tw/~yvchen/s105-icb/doc/170321_LU.pdf

原文地址:https://www.cnblogs.com/llhthinker/p/8978029.html

时间: 2024-11-27 04:22:51

“你什么意思”之基于RNN的语义槽填充(Pytorch实现)的相关文章

提速1000倍,预测延迟少于1ms,百度飞桨发布基于ERNIE的语义理解开发套件

11月5日,在『WAVE Summit+』2019 深度学习开发者秋季峰会上,百度对外发布基于 ERNIE 的语义理解开发套件,旨在为企业级开发者提供更领先.高效.易用的 ERNIE 应用服务,全面释放 ERNIE 的工业化价值,其中包含 ERNIE 轻量级解决方案,提速 1000倍! 今年 7 月,百度发布持续学习语义理解框架 ERNIE 2.0,在共计 16 个中英文任务上超越BERT.XLNET,取得了 SOTA 的效果. ERNIE2.0 发布以来,ERNIE 产业化应用进程不断加速,易

基于多种转换语义的图数据库查询

1. 摘要 因为图数据库的复杂模式和不同的信息描写叙述方式,对于非专业用户来说查询复杂的图数据库是异常困难的. 一个好的图查询引擎应该支持多种转化--同义词.缩略词.简写以及本体等等,而且应该可以对搜索结果进行一个非常好地排序. 基于此问题本文提出了一种新型的查询框架来方便用户查询,解放了为构造查询图而抓耳挠腮的用户群. 2. 应用背景 2.1 应用 图数据库也是一种流行的数据存储方式.如知识图.信息网络以及社交网络等应用的数据都存储在图数据库中.由于图数据的无模式或者模式太复杂以及信息的多种描

基于本体的语义信息模型的验证方法

一.先简单说说整个的一个需求吧 广义的配电管理系统(DMS)涵盖配电网生产.运行和服务全过程,是整个电力企业信息集成系统的一个有机组成部分.DMS 包含着大量应用系统,由于现在配网一体化和智能化发展的要求,需要这些应用系统之间能够相互的进行数据交换(实现系统间的互操作如下图),但这些大量的系统由于开发时间和功能的不一致,造成了这些系统和相应的数据库采用了不同的接口标准和模型,相对独立,不可避免的造成信息重叠和"信息孤岛",无法实现全局范围内的信息交互和信息共享. 目前,整个电网对此的解

深度学习之六,基于RNN(GRU,LSTM)的语言模型分析与theano代码实现

引言 前面已经介绍过RNN的基本结构,最基本的RNN在传统的BP神经网络上,增加了时序信息,也使得神经网络不再局限于固定维度的输入和输出这个束缚,但是从RNN的BPTT推导过程中,可以看到,传统RNN在求解梯度的过程中对long-term会产生梯度消失或者梯度爆炸的现象,这个在这篇文章中已经介绍了原因,对于此,在1997年 的Grave大作[1]中提出了新的新的RNN结构:Long Short Term Dependency.LSTM在传统RNN的基础上加了许多的"门",如input

用CNTK搞深度学习 (二) 训练基于RNN的自然语言模型 ( language model )

前一篇文章  用 CNTK 搞深度学习 (一) 入门    介绍了用CNTK构建简单前向神经网络的例子.现在假设读者已经懂得了使用CNTK的基本方法.现在我们做一个稍微复杂一点,也是自然语言挖掘中很火的一个模型: 用递归神经网络构建一个语言模型. 递归神经网络 (RNN),用图形化的表示则是隐层连接到自己的神经网络(当然只是RNN中的一种): 不同于普通的神经网络,RNN假设样例之间并不是独立的.例如要预测“上”这个字的下一个字是什么,那么在“上”之前出现过的字就很重要,如果之前出现过“工作”,

基于jq的表单填充

//表单填充 formDataLoad: function (domId, obj) { for (var property in obj) { if (obj.hasOwnProperty(property) == true) { if ($("#" + domId + " [name='" + property + "']").size() > 0) { $("#" + domId + " [name='&

使用深度RNN模型构建语义搜索引擎

/* 版权声明:可以任意转载,转载时请标明文章原始出处和作者信息 .*/ author: 张俊林 本文讨论如何用深度学习系统来构造语义搜索引擎的问题.这里所谓的语义搜索,是指的能做用户查询和搜索网页之间的语义级别匹配,比如说,用户输入"Iphone",虽然某篇文章讲到了"苹果公司正在试图做新型的手机",但是没有明确说iphone的字样,那么即使如此也能够将这篇文章找出来.传统的搜索引擎对于这种情况是无能为力的,因为它们基本上还是基于字面匹配作为排序的基础的,没有任何

基于本体的地学数据语义检索(简介)

硕士期间做了基于本体的地学数据语义检索方面的工作,首先是传统的全文检索查询600Ma(Ma表示百万年,这里都是指百万年前): 然后是基于本体的语义检索查询结果:(600Ma处于震旦纪.下震旦世.元古宙等地质年代时间内) 具体方法会在论文中论述,此处省略4万字...(请不要打脸)

基于PaddlePaddle框架利用RNN(循环神经网络)生成古诗句

基于PaddlePaddle框架利用RNN(循环神经网络)生成古诗句 在本项目中,将使用PaddlePaddle实现循环神经网络模型(即RNN模型,以下循环神经网络都称作RNN),并实现基于RNN语言模型进行诗句的生成. 本项目利用全唐诗数据集对RNN语言模型进行训练,能够实现根据输入的前缀诗句,自动生成后续诗句. 本实验所用全唐诗数据集下载地址:https://pan.baidu.com/s/1OgIdxjO2jh5KC8XzG-j8ZQ 1.背景知识 RNN是一个序列模型,基本思路是:在时刻