Theano(2) RNN训练词向量

一、项目简介

项目
Recurrent Neural Networks with Word Embeddings
教程地址:http://deeplearning.net/tutorial/rnnslu.html
Task
The Slot-Filling (Spoken Language Understanding)给句子中每个word分配标签,是一个分类问题。
Dataset
数据集是DARPA的一个小型数据集:ATIS (Airline Travel Information System),使用Inside Outside Beginning (IOB)表示
数据集中训练集句子4978个,word 56590个;测试集句子893,word 9198个;平均句长 15;The number of classes (different slots) is 128 including the O label (NULL).
注:B- prefix 实体的开始, I- prefix 实体内部,O tag 不属于任何实体
评价指标
Precision,Recall,F1 score
教程中使用conlleval PERL script(是一个计算上述指标的脚本代码)来评价性能

二、RNN简介

词向量
这里使用的词向量是context window word embeddings,即定义一个窗口大小,把句子中的每个word及其前后的word index提取出来,再把把 index 转换成 embeddings作为对应每个 word 的实数向量。

不同于传统的FNNs(Feed-forward Neural Networks,前向反馈神经网络),RNNs引入了定向循环,能够处理那些输入之间前后关联的问题。教程中使用的是(Elman) recurrent neural network (E-RNN),把当前时刻(t)的输入以及前一时刻( t-1)的隐藏层状态作为输入。

参数
E-RNN中需要学习的参数如下:
the word embeddings
the initial hidden state (real-value vector)
two matrices for the linear projection of the input t and the previous hidden layer state t-1(神经网络中的权重W)
(optional) bias. (偏置项,此处不用)
softmax classification layer on top
超参数如下:
dimension of the word embedding(de,50)
size of the vocabulary
number of hidden units(隐藏单元数量)
number of classes(类别数)
random seed + way to initialize the model(初始化模型种子和方式)

三、训练

更新
使用随机梯度下降,一个句子为一个minibatch,每一个句子更新一次参数。每次更新后,都要对 word embeddings 归一化。

停止条件
Early-stoppong,按给定数量的epochs训练,每次epoch后都在验证集上测试 F1 score,保留最佳模型。

超参选择
使用 KISS random search (没太看懂,是暴搜策略吗?)
教程附的超参如下:
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}

主要代码及注释

#coding=utf-8
import theano
import numpy
import os

from theano import tensor as T
from collections import OrderedDict

class RNNSLU(object):
    ''' elman neural net model '''
    def __init__(self, nh, nc, ne, de, cs):
        ''' nh :: dimension of the hidden layer nc :: number of classes ne :: number of word embeddings in the vocabulary de :: dimension of the word embeddings cs :: word window context size '''
        # parameters of the model
        self.emb = theano.shared(0.2 * numpy.random.uniform(-1.0, 1.0,\
                   (ne+1, de)).astype(theano.config.floatX)) # add one for PADDING at the end 首尾处的单词窗口记为-1
        self.Wx  = theano.shared(0.2 * numpy.random.uniform(-1.0, 1.0,\
                   (de * cs, nh)).astype(theano.config.floatX)) #连接nh隐藏单元与de个输入词(每个维度cs)
        self.Wh  = theano.shared(0.2 * numpy.random.uniform(-1.0, 1.0,\
                   (nh, nh)).astype(theano.config.floatX)) #隐藏层连接的也是隐藏层,nh*nh
        self.W   = theano.shared(0.2 * numpy.random.uniform(-1.0, 1.0,\
                   (nh, nc)).astype(theano.config.floatX)) #输出层 nh个隐藏层连接nc个输出单元
        self.bh  = theano.shared(numpy.zeros(nh, dtype=theano.config.floatX))#每个隐藏单元一个bia 共nh个
        self.b   = theano.shared(numpy.zeros(nc, dtype=theano.config.floatX))#每个输出单元一个bia 共nc类即nc个输出单元
        self.h0  = theano.shared(numpy.zeros(nh, dtype=theano.config.floatX))#定义一个t0时刻的输出,每个隐藏单元一个

        # bundle
        self.params = [self.emb, self.Wx, self.Wh, self.W, self.bh, self.b, self.h0 ]
        self.names = ['embeddings', 'Wx', 'Wh', 'W', 'bh', 'b', 'h0']
        idxs = T.imatrix() # as many columns as context window size/lines as words in the sentence
        x = self.emb[idxs].reshape((idxs.shape[0], de*cs)) #共n个输入,每个输入de个词(每个维度cs) 
        y = T.iscalar('y') # label

        def recurrence(x_t, h_tm1):
            h_t = T.nnet.sigmoid(T.dot(x_t, self.Wx) + T.dot(h_tm1, self.Wh) + self.bh)#隐藏层t时刻输出=f(输入*Wx+h_t-1*Wh+b)
            s_t = T.nnet.softmax(T.dot(h_t, self.W) + self.b)#输出层t时刻的输出
            return [h_t, s_t]
        #将某个函数作用于输入序列上,得到每一步输出的结果。 
        #和Reduction和map不同之处在于,scan在计算的时候,可以访问以前n步的输出结果,比较适合RNN
        [h, s],  = theano.scan(fn=recurrence, \
            sequences=x, outputs_info=[self.h0, None], \
            n_steps=x.shape[0])

        p_y_given_x_lastword = s[-1,0,:]
        p_y_given_x_sentence = s[:,0,:]
        y_pred = T.argmax(p_y_given_x_sentence, axis=1)

        #learning rate
        lr = T.scalar('lr')
        #cost and gradients
        nll = -T.log(p_y_given_x_lastword)[y]
        gradients = T.grad(nll, self.params)
        updates = OrderedDict((p, p-lr*g) for p, g in zip(self.params, gradients))

        # theano functions
        self.classify = theano.function(inputs=[idxs], outputs=y_pred)
        #训练
        self.train = theano.function( inputs = [idxs, y, lr],
                                      outputs = nll,
                                      updates = updates )
        #归一化
        self.normalize = theano.function( inputs = [],
                         updates = {self.emb:\
                         self.emb/T.sqrt((self.emb**2).sum(axis=1)).dimshuffle(0,'x')})
    #保存参数
    def save(self, folder):   
        for param, name in zip(self.params, self.names):
            numpy.save(os.path.join(folder, name + '.npy'), param.get_value())

你可能感兴趣的:(Theano(2) RNN训练词向量)