反向传播(BPTT)与循环神经网络(RNN)文本预测
来源:互联网 发布:rs232转网络 编辑:程序博客网 时间:2024/05/12 00:13
反向传播(BPTT)与循环神经网络(RNN)文本预测
BPTT 与 RNN文本预测
参考博客:
- http://www.wildml.com/2015/10/recurrent-neural-networks-tutorial-part-3-backpropagation-through-time-and-vanishing-gradients/
- http://www.wildml.com/2015/09/recurrent-neural-networks-tutorial-part-2-implementing-a-language-model-rnn-with-python-numpy-and-theano/
参考论文:
- A guide to recurrent neural networks and backpropagation
本文介绍简单Recurrent Neural Networks(RNN)的基本训练算法BACKPROPAGATION THROUGH TIME (BPTT),并用 python2.7 实现RNN的文本预测。
1. RNN训练方法:BACKPROPAGATION THROUGH TIME (BPTT)
基本RNN结构如下所示:
BPTT与传统的反向传播算法基本相同,包含如下三个步骤:
(1) 前向传播计算输出
(2) 反向传播计算误差
(3) 求解模型目标函数
(1) 前向计算
利用如下两式从输入层
(2) 误差
和
每一时刻
对于输出层
由于
对于其它层
由于
对于某一隐藏状态层,由
(3) 权重偏导数的计算
模型目标函数
模型目标函数
其中
模型目标函数
其中
2. RNN预测文本实战
本文采用dataset available on Google’s BigQuery的前10000条评论数据作为RNN的训练数据,然后基于训练后的RNN,生成新的评论文本。
(1) 数据预处理
文本标记
首先,将每个训练数据进行词语标记。比如 ‘who cares what you think ?’ 标记为[‘who’, ‘cares’, ‘you’, ‘think’, ‘?’]
这里会用到自然语言处理包nltk中的相关函数。
去除不常用词,标记词典中不存在的词
有些词语在所有句子中可能仅出现过几次,而词典的长度不能过大,否则会加长训练时间。因此,一种简单的做法是将不常用词从词典中去除。
对于词典中不存在的词,可统一标记为 ‘UNKNOWN_TOKEN’,并将 ‘UNKNOWN_TOKEN’ 加入词典。
标记每个样本句的开头和结尾
为便于训练,分别用’START_TOKEN’ 和 ‘END_TOKEN’ 标记每句话的开头和结尾,并将’START_TOKEN’ 和 ‘END_TOKEN’ 加入词典。
将文本转化为词向量
训练前,需将文本转化为数值向量,可将词典中的每个词进行编号。
比如,一个样本为 [‘START_TOKEN’, ‘who’, ‘cares’, ‘what’, ‘you’, ‘think’, ‘?’, ‘END_TOKEN’],其对于的数值向量为
(2) RNN实现
文本预处理,获取训练样本
首先实现 tokenFile2vector 类,tokenFile.py
import numpy as npimport nltk, itertools, csvTXTCODING = 'utf-8'unknown_token = 'UNKNOWN_TOKEN'start_token = 'START_TOKEN'end_token = 'END_TOKEN'# 解析评论文件为数值向量class tokenFile2vector: def __init__(self, file_path, dict_size): self.file_path = file_path self.dict_size = dict_size # 将文本拆成句子,并加上句子开始和结束标志 def _get_sentences(self): sents = [] with open(self.file_path, 'rb') as f: reader = csv.reader(f, skipinitialspace=True) # 去掉表头 reader.next() # 解析每个评论为句子 sents = itertools.chain(*[nltk.sent_tokenize(x[0].decode(TXTCODING).lower()) for x in reader]) sents = ['%s %s %s' % (start_token, sent, end_token) for sent in sents] print 'Get {} sentences.'.format(len(sents)) return sents # 得到每句话的单词,并得到字典及字典中每个词的下标 def _get_dict_wordsIndex(self, sents): sent_words = [nltk.word_tokenize(sent) for sent in sents] word_freq = nltk.FreqDist(itertools.chain(*sent_words)) print 'Get {} words.'.format(len(word_freq)) common_words = word_freq.most_common(self.dict_size-1) # 生成词典 dict_words = [word[0] for word in common_words] dict_words.append(unknown_token) # 得到每个词的下标,用于生成词向量 index_of_words = dict((word, ix) for ix, word in enumerate(dict_words)) return sent_words, dict_words, index_of_words # 得到训练数据 def get_vector(self): sents = self._get_sentences() sent_words, dict_words, index_of_words = self._get_dict_wordsIndex(sents) # 将每个句子中没包含进词典dict_words中的词替换为unknown_token for i, words in enumerate(sent_words): sent_words[i] = [w if w in dict_words else unknown_token for w in words] X_train = np.array([[index_of_words[w] for w in sent[:-1]] for sent in sent_words]) y_train = np.array([[index_of_words[w] for w in sent[1:]] for sent in sent_words]) return X_train, y_train, dict_words, index_of_words
基于以上实现的 tokenFile2vector 类,获得训练样本 X_train, y_train:
file_path = r'/results-20170508-103637.csv'dict_size = 8000myTokenFile = tokenFile2vector(file_path, dict_size)X_train, y_train, dict_words, index_of_words = myTokenFile.get_vector()
实现RNN
首先生成 myRNN类,myrnn.py
import tokenFileimport numpy as np# 输出单元激活函数def softmax(x): x = np.array(x) max_x = np.max(x) return np.exp(x-max_x) / np.sum(np.exp(x-max_x))class myRNN: def __init__(self, data_dim, hidden_dim=100, bptt_back=4): # data_dim: 词向量维度,即词典长度; hidden_dim: 隐单元维度; bptt_back: 反向传播回传时间长度 self.data_dim = data_dim self.hidden_dim = hidden_dim self.bptt_back = bptt_back # 初始化权重向量 U, W, V; U为输入权重; W为递归权重; V为输出权重 self.U = np.random.uniform(-np.sqrt(1.0/self.data_dim), np.sqrt(1.0/self.data_dim), (self.hidden_dim, self.data_dim)) self.W = np.random.uniform(-np.sqrt(1.0/self.hidden_dim), np.sqrt(1.0/self.hidden_dim), (self.hidden_dim, self.hidden_dim)) self.V = np.random.uniform(-np.sqrt(1.0/self.hidden_dim), np.sqrt(1.0/self.hidden_dim), (self.data_dim, self.hidden_dim)) # 前向传播 def forward(self, x): # 向量时间长度 T = len(x) # 初始化状态向量, s包含额外的初始状态 s[-1] s = np.zeros((T+1, self.hidden_dim)) o = np.zeros((T, self.data_dim)) for t in xrange(T): s[t] = np.tanh(self.U[:, x[t]] + self.W.dot(s[t-1])) o[t] = softmax(self.V.dot(s[t])) return [o, s] # 预测输出 def predict(self, x): o, s = self.forward(x) pre_y = np.argmax(o, axis=1) return pre_y # 计算损失, softmax损失函数, (x,y)为多个样本 def loss(self, x, y): cost = 0 for i in xrange(len(y)): o, s = self.forward(x[i]) # 取出 y[i] 中每一时刻对应的预测值 pre_yi = o[xrange(len(y[i])), y[i]] cost -= np.sum(np.log(pre_yi)) # 统计所有y中词的个数, 计算平均损失 N = np.sum([len(yi) for yi in y]) ave_loss = cost / N return ave_loss # 求梯度, (x,y)为一个样本 def bptt(self, x, y): dU = np.zeros(self.U.shape) dW = np.zeros(self.W.shape) dV = np.zeros(self.V.shape) o, s = self.forward(x) delta_o = o delta_o[xrange(len(y)), y] -= 1 for t in np.arange(len(y))[::-1]: # 梯度沿输出层向输入层的传播 dV += delta_o[t].reshape(-1, 1) * s[t].reshape(1, -1) # self.data_dim * self.hidden_dim delta_t = delta_o[t].reshape(1, -1).dot(self.V) * ((1 - s[t-1]**2).reshape(1, -1)) # 1 * self.hidden_dim # 梯度沿时间t的传播 for bpt_t in np.arange(np.max([0, t-self.bptt_back]), t+1)[::-1]: dW += delta_t.T.dot(s[bpt_t-1].reshape(1, -1)) dU[:, x[bpt_t]] = dU[:, x[bpt_t]] + delta_t delta_t = delta_t.dot(self.W.T) * (1 - s[bpt_t-1]**2) return [dU, dW, dV] # 计算梯度 def sgd_step(self, x, y, learning_rate): dU, dW, dV = self.bptt(x, y) self.U -= learning_rate * dU self.W -= learning_rate * dW self.V -= learning_rate * dV # 训练RNN def train(self, X_train, y_train, learning_rate=0.005, n_epoch=5): losses = [] num_examples = 0 for epoch in xrange(n_epoch): for i in xrange(len(y_train)): self.sgd_step(X_train[i], y_train[i], learning_rate) num_examples += 1 loss = self.loss(X_train, y_train) losses.append(loss) print 'epoch {0}: loss = {1}'.format(epoch+1, loss) # 若损失增加,降低学习率 if len(losses) > 1 and losses[-1] > losses[-2]: learning_rate *= 0.5 print 'decrease learning_rate to', learning_rate
训练RNN
rnn = myRNN(dict_size, hidden_dim=100, bptt_back=4)rnn.train(X_train[:200], y_train[:200], learning_rate=0.005, n_epoch=10)
可得如下结果:
epoch 1: loss = 8.97211993132epoch 2: loss = 8.93082011501epoch 3: loss = 6.7136525424epoch 4: loss = 6.21936677548epoch 5: loss = 6.00813231779epoch 6: loss = 5.87637648866epoch 7: loss = 5.78373455074epoch 8: loss = 5.71807642521epoch 9: loss = 5.63435329796epoch 10: loss = 5.56515764008
(3) 用RNN进行文本预测
基于训练好的RNN模型,我们可以得到下一个词将会是什么,从而生成新的文本。
unknown_token = 'UNKNOWN_TOKEN'start_token = 'START_TOKEN'end_token = 'END_TOKEN'def generate_text(rnn, dict_words, index_of_words): # dict_words: type list; index_of_words: type dict sent = [index_of_words[start_token]] # 预测新词,知道句子的结束(END_TOKEN) while not sent[-1] == index_of_words[end_token]: next_probs, _ = rnn.forward(sent) sample_word = index_of_words[unknown_token] # 按预测输出分布进行采样,得到新的词 while sample_word == index_of_words[unknown_token]: samples = np.random.multinomial(1, next_probs[-1]) sample_word = np.argmax(samples) # 将新生成的有含义的词(即不为UNKNOWN_TOKEN的词)加入句子 sent.append(sample_word) new_sent = [dict_words[i] for i in sent[1:-1]] new_sent_str = ' '.join(new_sent) return new_sent_str
生成文本举例:
sent_str = generate_text(rnn, dict_words, index_of_words)print 'Generate sentence:', sent_str
可得如下类似结果:
例子:it 's this he your wealth decisions roof of .mao dam , many are things a go a issue n't you works that a half
3. RNN中的梯度消失或爆炸问题
上边生成文本的结果可知,RNN生成的文本效果比较poor。一个可能的原因是训练数据不足、预处理不精细等等,然而,更重要的原因是RNN对于句子中跨度较大的词间的依赖关系无能为力。其实,从BPTT中可以发现,梯度的求解过程中存在这’消失的梯度’或’爆炸的梯度’问题。
消失的梯度:
比如,在
爆炸的梯度:
当
梯度消失问题解决方法:
目前已有一些解决梯度消失的方法,比如,合适的权值初始化;使用
更有效且受关注的解决方法是采用 Long Short-Term Memory (LSTM) 或 Gated Recurrent Unit (GRU) 结构,他们专为解决梯度消失而生。
RNN的介绍先到这里,后续跟进。
- 反向传播(BPTT)与循环神经网络(RNN)文本预测
- 循环神经网络(RNN)反向传播算法(BPTT)理解
- 循环神经网络(RNN)反向传播算法(BPTT)理解
- 循环神经网络(RNN)反向传播算法(BPTT)
- 循环神经网络(RNN)模型与前向反向传播算法
- 循环神经网络(RNN)模型与前向反向传播算法
- 循环神经网络(RNN)模型与前向反向传播算法
- 循环神经网络(RNN)模型与前向反向传播算法
- RNN与反向传播算法(BPTT)的理解
- RNN循环神经网络里的BPTT算法
- RNN循环神经网络中的权重更新算法-BPTT
- 深度学习——循环神经网络RNN(一)_反向传播算法
- 循环神经网络(rnn)的时间序列预测
- cs231n-反向传播与神经网络
- 神经网络正向传播与反向传播
- 循环神经网络(RNN)练习:比特币市场的分析与预测
- 神经网络的前向传播和误差反向传播(NN,RNN,LSTM)(一)
- 神经网络的前向传播和误差反向传播(NN,RNN,LSTM)(二)
- weka文本聚类(3)--文本转换成arff
- RecycleView.Adapter的适配方法
- UML建模
- PAT 1013 乙等(数素数)c++
- 文章标题
- 反向传播(BPTT)与循环神经网络(RNN)文本预测
- Java多线程基础(二)
- 寻觅web项目的lib下为什么会有weblogic.jar
- P1424小鱼的航程
- COJ-1005-Binary Search Tree analog
- kafka教程-基本概念
- HDU
- Java三大框架搭建
- boost库简单读写xml