英文原文请参考http://www.deeplearning.net/tutorial/rnnslu.html
总结
在本文中,你将学会:
- 词嵌套
- RNN架构
- 上下文窗口
in order to perform Semantic Parsing / Slot-Filling (Spoken Language Understanding)
Code
直接运行实验可以使用这个github部署资源。
任务
Slot-Filling是一个分类任务,给定一个句子,给每个word标记一个label
数据库
这个任务的benchmark是ATIS数据库(Airline Travel Information System)。用IOB表示。下面是例子
ATIS包含4978/893个句子,56590/9198个词(平均句子长度15)。类别数为128,包含label(NULL)
我们将未知的单词设为<UNK>,数字表示为DIGIT.
我们将官方训练数据分为训练和验证集。我们使用如下指标进行评估:
- 准确率
- 召回率
- F1 score
我们将使用PERL脚本来测量模型的性能。
递归神经网络模型
原始输入编码
a token对应于一个单词,ATIS中的每个token与一个index相连。每个句子是indexes的数组(int32)。(train,valid,test)是indexes数组的列表。定义一个python字典将indexes空间映射到words空间。如下:
这个句子的标签(labels)也类似,如下:
上下文窗口
给定一个句子,例如一个index的数组,和一个窗口大小如1,3,5...,我们需要将句子中的每个单词转换成包含这个特定单词的上下文窗口。例如:
其中index为-1对应于PADDING, PADDING是我们插入在句子前面或最后的。下面是例子:
输入是一个句子每个单词的index数组,输出为一个矩阵,每一行对应于句子中一个单词的上下文窗口。
词嵌套
既然我们已经将句子转换成上下文窗口(index的矩阵),我们必须将这些index关联到embeddings(每个单词真正的值向量)
x符号变量为矩阵,shape为(句子中的单词数量,嵌入空间的维度*上下文窗口大小(de*cs=50*5))
使用theano函数来完成这个过程:
现在我们得到了上下文窗口词嵌套(每行)的序列(序列长度为句子长度,上例为5)。
Elman RNN
前面,我们将输入处理成序列结构(sequential/temporal structure),row 0表示时间t=0步,row 1表示时间t=1步,等等。
需要学习的E-RNN参数为:
- 词嵌套(真值矩阵,real-valued matrix)
- 初始隐藏状态(real-value vector)
- two matrices for the linear projection of the input
t
and the previous hidden layer statet-1
- (可选)bias
- 最上面的softmax分类层
超参数定义了整个架构:
- 词嵌套的维度
- 词典大小
- 隐藏单元数
- 类别数
- 随机种子+初始化模型的方式
给出如下代码:
class myRNNSLU(object): def __init__(self, nh, nc, ne, de, cs): ''' :param nh: 隐藏层维度 :param nc: 类别数 :param ne: number of word embeddings in the vocabulary,词典中词的个数(eg.1000) :param de: 词嵌套的维度(eg.50) :param cs: 上下文窗口大小(eg.5) :return: ''' # 模型参数 # emb 嵌套向量 (1000+1, 50) self.emb = theano.shared(name='embeddings', value=0.2 * numpy.random.uniform(-1.0, 1.0, (ne + 1, de)) .astype(theano.config.floatX)) # wx 输入层权重 (50*5, nh) self.wx = theano.shared(name='wx', value=0.2 * numpy.random.uniform(-1.0, 1.0, (de * cs, nh)) .astype(theano.config.floatX)) # wh 隐藏层权重 (nh, nh) self.wh = theano.shared(name='wh', value=0.2 * numpy.random.uniform(-1.0, 1.0, (nh, nh)) .astype(theano.config.floatX)) # w 全连接权重 (nh, nc) self.w = theano.shared(name='w', value=0.2 * numpy.random.uniform(-1.0, 1.0, (nh, nc)) .astype(theano.config.floatX)) # bh 隐藏层偏差 self.bh = theano.shared(name='bh', value=numpy.zeros(nh, dtype=theano.config.floatX)) self.b = theano.shared(name='b', value=numpy.zeros(nc, dtype=theano.config.floatX)) self.h0 = theano.shared(name='h0', value=numpy.zeros(nh, dtype=theano.config.floatX)) self.params = [self.emb, self.wx, self.wh, self.w, self.bh, self.b, self.h0] # 根据嵌套矩阵构建输入 # idxs (一个句子长度,窗口大小5) # x (一个句子长度,50*5) 就是将对应word的index变为50维的词向量 # emb[idxs] 维度为(一个句子长度, 窗口大小5,维度50) idxs = T.imatrix() x = self.emb[idxs].reshape((idxs.shape[0], de*cs)) y_sentence = T.ivector('y_sentence') # labels def recurrence(x_t, h_tm1): # h_t (len, nh) h_t = T.nnet.sigmoid(T.dot(x_t, self.wx) + T.dot(h_tm1, self.wh) + self.bh) # s_t (len, nc) s_t = T.nnet.softmax(T.dot(h_t, self.w) + self.b) return [h_t, s_t] # n_steps 表示迭代的次数 # sequences 需要迭代的序列,会变化 # outputs_info 指定初值 # s (len, n_steps, nc) 是这样吗? [h, s], _ = theano.scan(fn=recurrence, sequences=x, outputs_info=[self.h0, None], n_steps=x.shape[0]) p_y_given_x_sentence = s[:,0,:] # (len, nc) 为什么是0 y_pred = T.argmax(p_y_given_x_sentence, axis=1) # 计算梯度,最大化似然函数 lr = T.scalar('lr') # 学习率 sentence_nll = - T.mean(T.log(p_y_given_x_sentence) [T.arange(x.shape[0]), y_sentence]) sentence_gradients = T.grad(sentence_nll, self.params) sentence_updates = OrderedDict((p, p - lr*g) for p, g in zip(self.params, sentence_gradients)) # theano functions self.classify = theano.function(inputs=[idxs], outputs=y_pred) self.sentence_train = theano.function(inputs=[idxs, y_sentence, lr], outputs=sentence_nll, updates=sentence_updates) # 每次更新后 normalize word embeddings self.normalize = theano.function(inputs=[], updates={self.emb: self.emb / T.sqrt((self.emb ** 2) .sum(axis=1)) .dimshuffle(0, 'x')})
理解一些基本操作
训练
更新
对于随机梯度下降更新,我们将整个句子视为一个mini-batch并且每个句子执行一次更新。
每次迭代/更新后,我们normalize词嵌套来keep them on a unit sphere。
超参数选择
- learning rate : uniform([0.05,0.01])
- window size : random value from {3,...,19}
- number of hidden units : random value from {100,200}
- embedding dimension : random value from {50,100}
参考资料: