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)

 





















免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM