前言
我打算基于lstm構(gòu)建一個分詞系統(tǒng),通過這個例子來學(xué)習(xí)下TensorFlow中如何訓(xùn)練循環(huán)遞歸神經(jīng)網(wǎng)絡(luò)。我們將從最粗糙的版本開始搭建這個小系統(tǒng),然后一步步優(yōu)化其中的每一部分,包括網(wǎng)絡(luò)架構(gòu)的優(yōu)化,數(shù)據(jù)處理的優(yōu)化,甚至整個代碼架構(gòu)的優(yōu)化。希望想我一樣的入門選手看到其中的每一步實現(xiàn)以及如何去優(yōu)化。
關(guān)于LSTM網(wǎng)絡(luò)的介紹,可以看官網(wǎng)推薦的一篇博客,寫的實在是太棒了http://colah.github.io/posts/2015-08-Understanding-LSTMs
另外這篇翻譯也很贊啊http://www.itdecent.cn/p/9dc9f41f0b29,這里不在詳述。
我們第一個版本的模型來自官網(wǎng)的tutorials中Recurrent Neural Networks部分內(nèi)容,官網(wǎng)的數(shù)據(jù)并不利于我們?nèi)ブ苯痈惺苣P陀?xùn)練的帶來的結(jié)果,所以后來我想了下用它來實現(xiàn)一個中文分詞,可能更有利于初學(xué)者去直觀的感受。第一個版本會我寫的很粗糙,主要是為了理解在TensorFlow中如何搭建LSTM網(wǎng)絡(luò)。
模型搭建
我對官網(wǎng)中的例子用我自己更喜歡的結(jié)構(gòu)重寫了下。 首先我們來看下如何搭建這個模型。開始我把模型部分代碼主要由inference(),loss()和training()三部分構(gòu)成,inference()部分負(fù)責(zé)模型搭建,loss()負(fù)責(zé)計算損失,為優(yōu)化做準(zhǔn)備,training()負(fù)責(zé)優(yōu)化部分,主要是對損失函數(shù)應(yīng)用梯度下降,更新參數(shù)。我把上面三部分寫封裝一個類里面。后來發(fā)現(xiàn)這樣實現(xiàn)會存在些問題,然后又把inference()的實現(xiàn)直接放在了類的init()函數(shù)里面。下面先看下模型的整體實現(xiàn),
class ptb_lstm():
def __init__(self,config):
...
def loss(self):
....
def train(self):
....
這里,我們在init()中傳了一個config類,這個config主要是一些模型參數(shù),大致形式是下面這樣,這篇筆記就不詳講了
class config():
'''參數(shù)配置類'''
init_scale = 0.1
learning_rate = 1.0
max_grad_norm = 5
num_layers = 2
hidden_size = 200
keep_prob = 1.0
lr_decay = 0.5
batch_size = 50
num_steps = 50
vocab_size = 3000
output_size = 3
learningrate = 0.5
好了,接下來我們先看init()部分
class lstm_model():
def __init__(self, config):
'''
:param config:config類,一些訓(xùn)練參數(shù)設(shè)置
'''
self.config = config
self.x = tf.placeholder(tf.int32, shape=(config.batch_size, config.num_steps))
self.y = tf.placeholder(tf.int32, shape=(config.batch_size, config.num_steps))
def lstm_cell():
#構(gòu)建lstm基本單元
return tf.contrib.rnn.BasicLSTMCell(
self.config.hidden_size, forget_bias=0.0, state_is_tuple=True)
attn_cell = lstm_cell
if config.keep_prob < 1:
#如果config.keep_prob參數(shù)小于1,對lstm單元進行dropout,防止過擬合
def attn_cell():
return tf.contrib.rnn.DropoutWrapper(
lstm_cell(), output_keep_prob=config.keep_prob)
cell = tf.contrib.rnn.MultiRNNCell(
[attn_cell() for _ in range(config.num_layers)], state_is_tuple=True)
#構(gòu)建多層的lstm,config.num_layers是層數(shù)參數(shù)
self._initial_state = cell.zero_state(self.config.batch_size, tf.float32)
#初始化lstm的state
with tf.device("/cpu:0"):
embedding = tf.get_variable(
"embedding", [self.config.vocab_size, self.config.hidden_size], dtype=tf.float32)
inputs = tf.nn.embedding_lookup(embedding,self.x)
#詞嵌入
outputs = []
state = self._initial_state
with tf.variable_scope("RNN"):
for time_step in range(self.config.num_steps):
if time_step > 0: tf.get_variable_scope().reuse_variables()
(cell_output, state) = cell(inputs[:, time_step, :], state)
outputs.append(cell_output)
#前向傳播,計算每個單元的cell_output和state,把cell_output添加到outputs,把state傳遞到下個單元,最終outputs的為(config.num_steps,config.batch_size,config.hidden_size)
output = tf.reshape(tf.concat(axis=1, values=outputs), [-1, self.config.hidden_size])
#output的形狀為(config.num_steps*config.batch_size,config.hedden_size)
softmax_w = tf.get_variable(
"softmax_w", [self.config.hidden_size, self.config.output_size], dtype=tf.float32)
softmax_b = tf.get_variable("softmax_b", [self.config.output_size], dtype=tf.float32)
self.logits = tf.matmul(output, softmax_w) + softmax_b
#得到最終的網(wǎng)絡(luò)輸出logits形狀為(config.num_steps*config.batch_size,config.output_size)
接著是loss(self,logits)
def loss(self):
logits = self.logits
loss = tf.contrib.legacy_seq2seq.sequence_loss_by_example(
[logits],
[tf.reshape(self.y, [-1])],
[tf.ones([self.config.batch_size * self.config.num_steps], dtype=tf.float32)])
# 交叉熵?fù)p失函數(shù),下一篇專門講下tensorflow中的幾個損失函數(shù)的實現(xiàn)
cost = tf.reduce_sum(loss) / self.config.batch_size
最后是后向傳播參數(shù)更新部分training(self)
def training(self):
loss = self.loss()
tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(loss, tvars),
self.config.max_grad_norm)
optimizer = tf.train.GradientDescentOptimizer(self.config.learningrate)
#優(yōu)化器
train_op = optimizer.apply_gradients(
zip(grads, tvars),
global_step=tf.contrib.framework.get_or_create_global_step())
#梯度下降
return train_op
模型部分就搭建完畢了,下一節(jié)我們來講下數(shù)據(jù)的預(yù)處理。