深度學習框架TensorFlow學習筆記(1)
本文為學習TensorFlow時的一些筆記和注意事項。
n
n
1.TensorFlow的基本使用
n
使用圖來表示計算任務
n在被稱之為會話(Session) 的上下文中執行圖
n
使用張量(Tensor)來表示數據
n通過變數(Variable)維護狀態
n使用feed和fetch可以為任意的操作賦值或者從其中獲取數據
n
n
n
n
n
n
上面這些話是copy的極客學院的tf的中文文檔。我對此的理解是,tf這個框架的運行方式,不同於以往我熟悉的C++,Python等語言。它是先構建出一個計算圖,這個圖的每個節點,就是一個操作,在操作中使用的參數稱作變數,存儲數據的結構叫做張量。
n
通俗點說,張量就是你讀入進來進行運算的數據,而變數,就是你在程序中聲明的一些參數,有固定的作用域。對於一個深度學習的網路來說,你首先需要構建出這個網路圖,然後創建一個會話,在會話中執行這個圖。
nimport tensorflow as tfnsess = tf.InteractiveSession() #創建會話nninit = tf.global_variables_initializer() #創建初始化的節點nsess.run(init) #執行這個節點的操作n
n
一些基本的操作就不說了,tf使用的版本是1.0,一些老版本的方法已經不一樣了,在這也做一點記錄。下面說一下tf里非常重要的變數作用域以及共享變數的問題。
n2. 變數作用域和共享變數
n
為什麼需要使用變數作用域和共享變數呢?因為如果你不使用,每個變數都使用tf.Variable()創建,那麼對於一個有3層的神經網路,你就需要寫3個weights和3個biases,如果這個網路有100層,那你就需要寫100*2個變數。
n看了不少論文的源碼,發現大家都使用了變數作用域的機制。tf中的這種機制主要由兩部分組成:
ntf.get_variable(<name>, <shape>, <initialzier>) #創建或者獲取一個變數ntf.variable_scope(<scope_name>) #指定命名空間n
n
對於tf.get_variable()方法,如果一個變數不存在,你就會創建一個變數,如果存在的話,它就會檢測是否共享,如果已經共享,那麼就會獲取以前創建的,否則就會報ValueError,比如:(此例摘自官方文檔)
n
def my_image_filter(input_images):n with tf.variable_scope("conv1"):n # Variables created here will be named "conv1/weights", "conv1/biases".n relu1 = conv_relu(input_images, [5, 5, 32, 32], [32])n with tf.variable_scope("conv2"):n # Variables created here will be named "conv2/weights", "conv2/biases".n return conv_relu(relu1, [5, 5, 32, 32], [32])nnresult1 = my_image_filter(image1)nresult2 = my_image_filter(image2)n# Raises ValueError(... conv1/weights already exists ...)n
n
這個錯誤我也是碰到了好多遍,花了好久理解和調試,才解決了。上述問題是因為沒有指定共享變數,所以只要使用reuse_variables()方法來指定共享即可。
nresult1 = my_image_filter(image1)ntf.get_variable_scope().reuse_variables() #在作用域中共享變數nresult2 = my_image_filter(image2)n
n
如果想要深入理解這兩個方法是如何工作的,可以參考TensorFlow官方文檔中文版
n我在寫代碼中遇到的一些問題,如下:
這個問題造成的原因是,這個變數已經創建過了,並且我的代碼裡面沒有做任何共享變數的操作,所以就炸裂了,但是我在每個variable_scope中都加了reuse = True之後,又提示出了變數不存在的錯誤。這是因為,第一次創建變數,其實是在上面全局變數初始化的地方做的,也就是:n
sess = tf.InteractiveSession() ninit = tf.global_variables_initializer() nsess.run(init)n
n
實際上第一次變數創建的地方是在sess.run(init),而我所有的reuse都設置為了True,表示一直都是去獲取以前的變數。而我在init的時候並沒有創建過變數呢,所以就出現了不存在的錯誤。並且要提及的一點是,在下面train的過程中,是一輪一輪的訓練,和共享變數是沒有關係的,所以不用擔心在train的時候會造成變數已經存在的問題。
當然,github上很多代碼,都沒有設置reuse = True這個參數,為什麼沒有出現變數已存在的問題呢?n正如我上面所說,正常情況起初是變數都不存在的,在全局初始化的時候,變數被創建了,然後在下面的train中,也不會出現變數已存在的錯誤,所以是可以正常運行的。那我為啥出現了錯誤呢,我想應該是與我使用的IDE有關吧,我是用的是ipython notebook,所以說珍愛生命,在用TensorFlow的時候不要用ipython notebook。
n為了避免上述的ValueError的問題,我在代碼中進行了改進,如下是我在寫CNN中的卷積層的代碼:
n
def conv2d(value, output_dim, k_h = 5, k_w = 5, strides = [1,2,2,1], name = conv2d):n with tf.variable_scope(name):n try:n weights =tf.get_variable( weights,n [k_h, k_w, value.get_shape()[-1], output_dim],n initializer = tf.truncated_normal_initializer(stddev = 0.02))n biases = tf.get_variable( biases, n [output_dim],initializer = tf.constant_initializer(0.0))n except ValueError:n tf.get_variable_scope().reuse_variables()n weights =tf.get_variable( weights,n [k_h, k_w, value.get_shape()[-1], output_dim],n initializer = tf.truncated_normal_initializer(stddev = 0.02))n biases = tf.get_variable( biases, n [output_dim],initializer = tf.constant_initializer(0.0))n conv = tf.nn.conv2d(value, weights, strides = strides, padding = SAME)n conv = conv + biasesn return convn
n
使用了try之後,如果碰到ValueError,就共享一下變數,這樣就可以防止各種莫名原因導致的錯誤了。
n3.模型的存儲
n
可以使用tf.train.Saver()來創建一個Saver管理模型中的變數
nsess = tf.InteractiveSession() #創建會話ninit_op = tf.global_variables_initializer() #創建初始化的節點nsaver = tf.train.Saver() #創建Savernsess.run(init_op) #初始化所有變數nsave_path = saver.save(sess, "/tmp/model.ckpt") #保存圖中所有的變數n
n
存儲之後,文件夾里會出現4個文件,所有的變數會存儲在checkpoint文件中,並且是以name和值的形式存儲(name是指創建時候的參數name)
n恢復變數
nsaver = tf.train.Saver()nsaver.restore(sess, "/tmp/model.ckpt") #需要存在同名的變數,不然需要在聲明saver是用一個dictionary設定n
n
下面是我自己的例子,前面已經保存過一個model.ckpt了,裡面的v1為全0
nimport tensorflow as tfnimport os nnv1 = tf.Variable(tf.constant(1.0, shape = [5]), name="v1")nv2 = tf.Variable(tf.truncated_normal(shape = [3,2], stddev=0.02), name="v2")nninit_op = tf.global_variables_initializer()nsess = tf.InteractiveSession()nsaver = tf.train.Saver()nsess.run(init_op)nprint(sess.run(v1)) #輸出[1,1,1,1,1]nsaver.restore(sess, "data/model.ckpt")nprint(sess.run(v1)) #輸出[0,0,0,0,0]n
n
再測試一下修改了name之後讀取變數的例子:
nimport tensorflow as tfnimport os nnv1 = tf.Variable(tf.constant(1.0, shape = [5]), name="v1")nv2 = tf.Variable(tf.truncated_normal(shape = [3,2], stddev=0.02), name="v2")nninit_op = tf.global_variables_initializer()nsess = tf.InteractiveSession()nsaver = tf.train.Saver({"vv1":v1}) #變數v1的name修改為vv1nsess.run(init_op)nsaver.save(sess, "data/model.ckpt")n
n
import tensorflow as tfnimport os nnv1 = tf.Variable(tf.constant(0.0, shape = [5]), name="v1")nv2 = tf.Variable(tf.truncated_normal(shape = [3,2], stddev=0.02), name="v2")nninit_op = tf.global_variables_initializer()nsess = tf.InteractiveSession()nsaver = tf.train.Saver({"vv1":v1})nsess.run(init_op)nprint(sess.run(v1)) #輸出[0,0,0,0,0]nsaver.restore(sess, "data/model.ckpt")nprint(sess.run(v1)) #輸出[1,1,1,1,1]n
n
首先我在第一個代碼中,存儲name為vv1,值為1.0的一個向量,第二個代碼中,添加的dictionary的意思是name為vv1的變數的值賦給v1變數,所以就產生了上述結果。
我們發現,其實 創建 saver對象時使用的鍵值對就是表達了一種對應關係:n- save時, 表示:variable的值應該保存到 checkpoint文件中的哪個 key下
- restore時,表示:checkpoint文件中key對應的值,應該restore到哪個variable
n
n
4.TensorBoard可視化
n
這部分內容,我也僅僅是嘗試了一下一些源碼,自己還沒寫過類似的。
nd_loss_real_sum = tf.summary.scalar("d_loss_real", d_loss_real)nd_loss_fake_sum = tf.summary.scalar("d_loss_fake", d_loss_fake)nd_loss_sum = tf.summary.scalar("d_loss", d_loss)ng_loss_sum = tf.summary.scalar("g_loss", g_loss)n
n
tf.summary.scalar()用於生成標量數據的summary,如下圖所示:
tf.summary.image(tag, tensor, max_images=3, collections=None, name=None):tensor,必須4維,形狀[batch_size, height, width, channels],max_images(最多只能生成3張圖片的summary),覺著這個用在卷積中的kernel可視化很好用.max_images確定了生成的圖片是[-max_images: ,height, width, channels],還有一點就是,TensorBord中看到的image summary永遠是最後一個global step的。nG_sum = tf.summary.image("G", G)n
n
在tensorboard中顯示是這樣的:
tensorboard還可以生成整個計算圖,雖然我還不是很清楚怎麼讀這張圖n在代碼里,首先要用FileWriter創建一個file writer用來向硬碟寫summary數據
nwriter = tf.summary.FileWriter(train_dir, sess.graph)n
n
然後每一次有參數被修改,都要用add_summary向FileWriter對象的緩存中存放event data。
n如果使用writer.add_summary(summary,global_step)時沒有傳global_step參數,會使scarlar_summary變成一條直線。
n在代碼中寫好了這些之後,就可以在命令行里啟動tensorboard了,命令是
ntensorboard --logdir="logs/" #放在當前文件夾的logs文件夾下n
n
然後在瀏覽器中打開http://192.168.1.109:6006就好了推薦閱讀:
TAG:TensorFlow | 深度学习DeepLearning | 人工智能 |