import osimport theano, numpyfrom theano import tensor as Tfrom collections import OrderedDictclass RNNSLU(object): """ Elman神经网络""" def __init__(self, nh, nc, ne, de, cs):"""用于初始化的超参数nh : 隐藏层的维度nc : 类别数(标签)ne : 词汇表大小de : 嵌入维度cs : 词上下文窗口大小"""# 要学习的参数:词嵌入self.embeddings = theano.shared(name='embeddings', value = 0.2 * numpy.random.uniform(-1.0, 1.0, (ne + 1, de)) .astype(theano.config.floatX))# 要学习的参数:将输入映射到隐藏层的权重矩阵(de*cs x nh)self.wx = theano.shared(name='wx', value = 0.2 * numpy.random.uniform(-1.0, 1.0, (de * cs, nh)) .astype(theano.config.floatX))# 要学习的参数:将前一个时间步的隐藏层映射到当前隐藏层的权重矩阵self.wh = theano.shared(name='wh', value = 0.2 * numpy.random.uniform(-1.0, 1.0, (nh, nh)) .astype(theano.config.floatX))# 要学习的参数:将隐藏层映射到输出层的权重矩阵(nh x nc)self.w = theano.shared(name='w', value = 0.2 * numpy.random.uniform(-1.0, 1.0, (nh, nc)) .astype(theano.config.floatX))# 要学习的参数:隐藏层的偏置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))# 要学习的参数:时间t=0时的隐藏层self.h0 = theano.shared(name='h0', value = numpy.zeros(nh, dtype=theano.config.floatX))# 捆绑参数self.params = [self.embeddings, self.wx, self.wh, self.w, self.bh, self.b, self.h0]self.names = ['embeddings', 'Wx', 'Wh', 'W', 'bh', 'b', 'h0']#编译训练函数self.prepare_train(de, cs)def prepare_train(self, de, cs):"""训练循环神经网络"""idxs = T.imatrix() # 列数 = 窗口中的词数,行数 = 句子长度# 准备接收输入和输出标签x = self.embeddings[idxs].reshape((idxs.shape[0], de*cs))y = T.iscalar('y') def recurrence(x_t, h_tm1): """ x_t : 时间t的输入 h_tm1 : 时间t-1的隐藏状态 """ # 计算时间t的隐藏状态 # h_t = g(x_t . w_x + h_tm1 . w_h + b_h) h_t = T.nnet.sigmoid(T.dot(x_t, self.wx) + T.dot(h_tm1, self.wh) + self.bh) # 计算输出层 # s_t = g(h_t . w + b) s_t = T.nnet.softmax(T.dot(h_t, self.w) + self.b) return [h_t, s_t][h,s], _ = theano.scan(fn=recurrence, sequences=x, outputs_info=[self.h0, None], n_steps=x.shape[0])#print h.ndim#print s.ndim# TODO: s的结构是什么?选择轴的作用是什么?p_y_given_sentence = s[:,0,:]y_pred = T.argmax(p_y_given_sentence, axis=1)# 学习率lr = T.scalar('lr')# 句子负对数似然(目标函数)sentence_nll = - T.mean(T.log(p_y_given_sentence)[T.arange(x.shape[0]), y])# 计算参数梯度sentence_gradients = T.grad(sentence_nll, self.params)# 计算更新sentence_updates = OrderedDict((p, p - lr*g) for p,g in zip(self.params, sentence_gradients))# 编译函数self.classify = theano.function(inputs=[idxs], outputs=y_pred)self.sentence_train = theano.function(inputs=[idxs, y, lr], outputs=sentence_nll, updates=sentence_updates)#### 从中调用类的主要函数rnn = RNNSLU(nh=s['nhidden'], nc=nClasses, ne=vocSize, de=s['emb_dimension'], cs=s['win'])for word_batch, label_last_word in zip(words, labels): rnn.sentence_train(word_batch, label_last_word, s['clr']) rnn.normalize()
代码解释:
我知道在StackOverflow上这样做不是一个好主意。但我已经努力了一个多星期来解码这个用于训练循环神经网络的代码。我首先是Theano的新手。
word_batch = array([[ -1, -1, -1, 194, 358, 463, 208]], dtype=int32)label_last_word = 126
word_batch是像下面这样的句子的索引:
‘我正在从英国去美国’
这里的word_batch是与特定词(如美国)相关联的上下文窗口。所以,如果上下文窗口大小为七,word batch中的中间值(194)代表该词在数据集中的索引。我想知道,当我将这个作为参数传递给rnn.sentence_train时,RNNSLU类内部是如何进行训练的。我对类中idx、x等变量的使用感到困惑。我知道理论上是如何发生的,但无法明确解码Theano部分。如果我的问题没有意义,请告诉我。
谢谢。
回答:
rnn.sentence_train
是具有 updates=sentence_updates
的Theano函数。这意味着每次调用 rnn.sentence_train
时,sentence_updates
字典键中的所有共享变量都将根据相应的 sentence_updates
字典值中的符号更新表达式进行更新。这些表达式都是经典的梯度下降(当前参数值 – 学习率 * 相对于参数的成本梯度)。
idxs
是训练函数输入的符号占位符。在您的示例中,word_batch
在调用训练函数时填充该占位符。