zoukankan      html  css  js  c++  java
  • Tensorflow多层LSTM代码分析

    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的问题,让权重的更新限制在一个合适的范围。

    具体的实现细节:

    1. 在solver中先设置一个clip_gradient 
    2. 在前向传播与反向传播之后,我们会得到每个权重的梯度diff,这时不像通常那样直接使用这些梯度进行权重更新,而是先求所有权重梯度的平方和再求根号sumsq_diff,如果sumsq_diff > clip_gradient,则求缩放因子scale_factor = clip_gradient / sumsq_diff。这个scale_factor在(0,1)之间。
    3. 最后将所有的权重梯度乘以这个缩放因子,这时得到的梯度才是最后的梯度信息。
    4. 这样就保证了在一次迭代更新中,所有权重的梯度的平方和在一个设定范围以内,这个范围就是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)



















  • 相关阅读:
    js中url跳转问题
    代码走查整理总结
    关于前后端分离跨域请求问题
    mysql大小写敏感问题
    初识react
    mysql测试
    关于使用Ajax请求json数据,@RequestMapping返回中文乱码的几种解决办法
    ssm实现分页查询
    js表单验证处理和childNodes 和children 的区别
    javaScript数组操作整理
  • 原文地址:https://www.cnblogs.com/qniguoym/p/7765725.html
Copyright © 2011-2022 走看看