1.tf.Graph()
你一旦开始你的任务,就已经有一个默认的图已经创建好了。而且可以通过调用tf.get_default_graph()
来访问到。
添加一个操作到默认的图里面,只要简单的调用一个定义了新操作的函数就行。比如下面的例子展示的:
import tensorflow as tf import numpy as np c=tf.constant(value=1) print(c.graph) print(tf.get_default_graph())
<tensorflow.python.framework.ops.Graph object at 0x107d38fd0>
<tensorflow.python.framework.ops.Graph object at 0x107d38fd0>
另外一种典型的用法就是要使用到Graph.as_default()
的上下文管理器( context manager),它能够在这个上下文里面覆盖默认的图。如下例:
import tensorflow as tf import numpy as np c=tf.constant(value=1) print(c.graph) print(tf.get_default_graph()) g=tf.Graph() print("g:",g) with g.as_default(): d=tf.constant(value=2) print(d.graph) print(g) <tensorflow.python.framework.ops.Graph object at 0x10b0e56d8> <tensorflow.python.framework.ops.Graph object at 0x10b0e56d8> g: <tensorflow.python.framework.ops.Graph object at 0x10b0df2e8> <tensorflow.python.framework.ops.Graph object at 0x10b0df2e8> <tensorflow.python.framework.ops.Graph object at 0x10b0df2e8>
2.tf.variable_scope()
利用TensorFlow 提供了变量作用域 机制,当构建一个视图时,很容易就可以共享命名过的变量.
变量作用域机制在TensorFlow中主要由两部分组成:
tf.get_variable(<name>, <shape>, <initializer>)
: 通过所给的名字创建或是返回一个变量.tf.variable_scope(<scope_name>)
: 通过tf.get_variable()
为变量名指定命名空间.
3.
tf.nn.rnn_cell.BasicLSTMCell(num_units=,forget_bias=,state_is_tuple=) #继承了RNNCell,state_is_tuple官方建议设置为True,此时输入和输出的states为c #(cell状态)和h(输出)的二元组 #输入输出和cell的维度相同,都是batch_size*num_units
4.
lstm_cell=tf.nn.rnn_cell.DropoutWrapper(lstm_cell,output_keep_prob=config.keep_prob) #对于rnn的部分不进行dropout,也就是从t-1时候的状态传递到t时刻进行计算时,这个中间不进行 #memory的dropout,仅在同一个t时刻,多层cell之间传递信息的时候进行dropout
5.
cell=tf.nn.rnn_cell.MultiRNNCell([lstm_cell]*config.num_layers,state_is_tuple=True) #多层lstm cell堆叠起来
tensorflow并不是简单的堆叠了多了single cell,而是将这些cell stack之后当成了一个完整的独立的cell,每个小cell的中间状态还是保存下来了,按照n_tuple存储,但是输出output只用最后那个cell的输出。
这样就定义好了每个t时刻的整体cell,接下来只要每个时刻传入不同的输入,再在时间上展开,就可以得到多个时间上的unroll graph
6.
self._inital_state=cell.zero_state(batch_size,data_type()) #我们刚刚定义好的cell会依次接收num_steps个输入然后产生最后的state(n-tuple,n表示堆叠的层数) #最后需要[batch_size,堆叠的层数]来存储seq的状态
7.tf.Variable & tf.get_variable()
使用tf.Variable时,如果检测到命名冲突,系统会自己处理。使用tf.get_variable()时,系统不会处理冲突,而会报错。
所以当我们需要共享变量的时候,使用tf.get_variable()
由于tf.Variable()
每次都在创建新对象,所有reuse=True
和它并没有什么关系。对于get_variable()
,来说,如果已经创建的变量对象,就把那个对象返回,如果没有创建变量对象的话,就创建一个新的。
8.gradient clipping(修剪)的引入是为了处理gradient explosion或者gradient vanishing的问题,让权重的更新限制在一个合适的范围。
具体的实现细节:
- 在solver中先设置一个clip_gradient
- 在前向传播与反向传播之后,我们会得到每个权重的梯度diff,这时不像通常那样直接使用这些梯度进行权重更新,而是先求所有权重梯度的平方和再求根号sumsq_diff,如果sumsq_diff > clip_gradient,则求缩放因子scale_factor = clip_gradient / sumsq_diff。这个scale_factor在(0,1)之间。
- 最后将所有的权重梯度乘以这个缩放因子,这时得到的梯度才是最后的梯度信息。
-
这样就保证了在一次迭代更新中,所有权重的梯度的平方和在一个设定范围以内,这个范围就是clip_gradient.
tf.clip_by_global_norm(t_list, clip_norm, use_norm=None, name=None)
t_list是梯度张量,clip_norm是截取的比率,和上面的clip_gradient是相同的东西,返回截取过后的梯度张量和一个所有张量的全局范数。
t_list的更新公式是:
t_list[i] * clip_norm / max(global_norm, clip_norm)
global_norm是所有梯度的平方和再求根号。
9.
from __future__ import absolute_import from __future__ import division from __future__ import print_function import tensorflow as tf import numpy as np import math import gzip import os import tempfile import time flags=tf.app.flags logging=tf.logging flags.DEFINE_string(#这里定义model的值是small 'model','small','A type of model.Possible options are:small,medium,large.' ) flags.DEFINE_string('data_path','/Users/guoym/Desktop/modles-master','data_path') flags.DEFINE_bool('use_fp16',False,'Train using 16-bit floats instead oof 32bit floats') FLAGS=flags.FLAGS def data_type(): return tf.float16 if FLAGS.use_fp16 else tf.float32 class PTBModel(object): def __init__(self,is_training,config): ''' 参数is_training:是否要进行训练,如果为False,则不会进行参数的修正 ''' self.batch_size = batch_size = config.batch_size self.num_steps = num_steps = config.num_steps size = config.hidden_size vocab_size = config.vocab_size self._input_data = tf.placeholder(tf.int32, [batch_size, num_steps]) # 输入 self._targets = tf.placeholder(tf.int32, [batch_size, num_steps]) # 预期输出,两者都是index序列,长度为num_step lstm_cell=tf.nn.rnn_cell.BasicLSTMCell(size,forget_bias=0.0,state_is_tuple=True) #num_units是指LSTM cell中的单元的数量 if is_training and keep_prob<1:#在外面包裹一层dropout lstm_cell=tf.nn.rnn_cell.DropoutWrapper(lstm_cell,output_keep_prob=config.keep_prob) #对于rnn的部分不进行dropout,也就是从t-1时候的状态传递到t时刻进行计算时,这个中间不进行 #memory的dropout,仅在同一个t时刻,多层cell之间传递信息的时候进行dropout cell=tf.nn.rnn_cell.MultiRNNCell([lstm_cell]*config.num_layers,state_is_tuple=True) #多层lstm cell堆叠起来 self._inital_state=cell.zero_state(batch_size,data_type()) #我们刚刚定义好的cell会依次接收num_steps个输入然后产生最后的state(n-tuple,n表示堆叠的层数) #最后需要[batch_size,堆叠的层数]来存储seq的状态 with tf.device("/cpu:0"): embedding=tf.get_variable("embedding",[vocab_size,size],dtype=data_type()) #将输入序列用embedding表示 shape=[batch,steps,hidden_size] inputs=tf.nn.embedding_lookup(embedding,self._input_data) if is_training and config.keep_prob<1: inputs=tf.nn.dropout(inputs,keep_prob) outputs=[] state=self._initial_state#state表示各个batch中的状态 with tf.variable_scope("RNN"): for time_step in range(num_steps): if time_step>0: tf.get_variable_scope().reuse_variables #当前变量作用域可以用tf.get_variable_scope()进行检索并且reuse 标签可以通过调用tf.get_variable_scope().reuse_variables()设置为True . (cell_output,state)=cell(inputs[:,time_step,:],state) #cell_output 是[batch,hidden_size] outputs.append(cell_output) #把之前的list展开,把[batch,hidden_size*num_steps] reshape 成[batch*numsteps,hiddensize] output=tf.reshape(tf.concat(1,outputs),[-1,size]) softmax_w=tf.get_variable('softmax_w',[size,vocab_size],dtype=data_type()) softmax_b=tf.get_variable('softmax_b',[vocab_size],dtype=data_type()) logits=tf.matmul(output,softmax_w)+softmax_b loss=tf.nn.seq2seq.sequence_loss_by_example( [logits], [tf.reshape(self._targets,[-1])], [tf.ones([batch_size*num_steps],dtype=data_type())])#展开成为一维的列表 self._cost=cost=tf.reduce_sum(loss)/batch_size#计算得到每批次的误差 self._final_state=state #logits是一个二维的张量,a*btargets就是一个一维的张量,长度为a,并且targets中的元素是不能 #超过b的整形,weights是一个一维的长度为a的张量。 #其意义就是针对logits中的每一个num_step,即[batch,vocab_size],对所有vocab_size个预测结果, #得出预测值最大的那个类别,与targets中的值相比较计算loss值 if not is_training: return self._lr=tf.Variable(0.0,trainable=True) tvars=tf.trainable_variables()#返回的是需要训练的张量的列表 grads,_=tf.clip_by_global_norm(tf.gradient(cost,tvars),config.max_grad_norm) optimizer=tf.train.GradientDescentOptimizer(self._lr) self._train_op=optimizer.apply_gradients(zip(grads,tvars))#将梯度应用于变量 self._new_lr=tf.placeholder(f.float32,shape=[],name='new_learning_rate') #用于外部向graph输入新的lr的值 self._lr_update=tf.assign(self._lr,self._new_lr) def assign_lr(self,session,lr_value): #使用session来调用lr_update操作 session.run(self._lr_update,feed_dict={self._new_lr:lr_value}) @property def input_data(self): return self._input_data @property def targets(self): return self._targets @property def initial_state(self): return self._initial_state @property def cost(self): return self._cost @property def final_state(self): return self._final_state @property def lr(self): return self._lr @property def train_op(self): return self._train_op def run_epoch(session,model,data,eval_op,verbose=False): #epoch_size表示批次的总数,也就是说,需要向session喂这么多次的数据 epoch_size=((len(data)//model.batch_size-1)//model.num_steps)#//表示整数除法 start_time=time.time() costs=0.0 iters=0 state=session.run(model.initial_state) for step,(x,y) in enumerate(reader.ptb_iterator(data,model.batch_size,model.num_steps)): fetchs=[model.cost,model.final_state,eval_op]#要进行的操作,注意训练时和其他时候的eval_op的区别 feed_dict={} feed_dict[model.input_data]=x feed_dict[model.targets]=y for i ,(c,h) in enumerate(model.initial_state): feed_dict[c] = state[i].c feed_dict[h] = state[i].h cost,state=session.run(fetch,feed_dict) costs+=cost iters+=model.num_steps if verbose and step % (epoch_size // 10) == 10: # 也就是每个epoch要输出10个perplexity值 print("%.3f perplexity: %.3f speed: %.0f wps" % (step * 1.0 / epoch_size, np.exp(costs / iters), iters * model.batch_size / (time.time() - start_time))) return np.exp(costs / iters) class SmallConfig(object): init_scale = 0.1 # learning_rate = 1.0 # 学习速率 max_grad_norm = 5 # 用于控制梯度膨胀, num_layers = 2 # lstm层数 num_steps = 20 # 单个数据中,序列的长度。 hidden_size = 200 # 隐藏层规模 max_epoch = 4 # epoch<max_epoch时,lr_decay值=1,epoch>max_epoch时,lr_decay逐渐减小 max_max_epoch = 13 # 指的是整个文本循环13遍。 keep_prob = 1.0 lr_decay = 0.5 # 学习速率衰减 batch_size = 20 # 每批数据的规模,每批有20个。 vocab_size = 10000 # 词典规模,总共10K个词 if __name__=='__main__': raw_data=reader.ptb_raw_data(FLAGS.data_path) train_data,valid_data,test_data,_=raw_data config=SmallConfig() eval_config=SmallConfig() eval_config.batch_size=1 eval_config.num_steps=1 with tf.Graph().as_default(),tf.Session() as session: initializer=tf.random_uniform_initializer(-config.init_scale,config.init_scale) #生成均匀分布的随机数,参数minval,maxval with tf.variable_scope('model',reuse=None,initializer=initializer): m=PTBModel(is_training=True,config=config)#训练模型 with tf.variable_scope('model',reuse=True,initializer=initializer):#交叉检验和测试模型 mvalid=PTBModel(is_training=False,config=config) mtest=PTBModel(is_training=False,config=eval_config) summary_writer = tf.summary.FileWriter('/tmp/lstm_logs',session.graph) tf.initialize_all_variables().run() # 对参数变量初始化 for i in range(config.max_max_epoch): #learning rate衰减 # 遍数<max epoch时,lr_decay=1l >max_epoch,lr_decay=0.5^(i-max_epoch) lr_decay = config.lr_decay ** max(i - config.max_epoch, 0.0) m.assign_lr(session, config.learning_rate * lr_decay) # 设置learning rate print("Epoch: %d Learning rate: %.3f" % (i + 1, session.run(m.lr))) train_perplexity = run_epoch(session, m, train_data, m.train_op,verbose=True) # 训练困惑度 print("Epoch: %d Train Perplexity: %.3f" % (i + 1, train_perplexity)) valid_perplexity = run_epoch(session, mvalid, valid_data, tf.no_op()) # 检验困惑度 print("Epoch: %d Valid Perplexity: %.3f" % (i + 1, valid_perplexity)) test_perplexity = run_epoch(session, mtest, test_data, tf.no_op()) # 测试困惑度 print("Test Perplexity: %.3f" % test_perplexity)