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)