深度學習框架TensorFlow學習筆記(1)

本文為學習TensorFlow時的一些筆記和注意事項。

n

n

1.TensorFlow的基本使用

n

  • 使用圖來表示計算任務

    n
  • n

  • 在被稱之為會話(Session) 的上下文中執行圖

    n

  • n

  • 使用張量(Tensor)來表示數據

    n
  • n

  • 通過變數(Variable)維護狀態

    n
  • n

  • 使用feed和fetch可以為任意的操作賦值或者從其中獲取數據

    n
  • n

上面這些話是copy的極客學院的tf的中文文檔。我對此的理解是,tf這個框架的運行方式,不同於以往我熟悉的C++,Python等語言。它是先構建出一個計算圖,這個圖的每個節點,就是一個操作,在操作中使用的參數稱作變數,存儲數據的結構叫做張量。

n

通俗點說,張量就是你讀入進來進行運算的數據,而變數,就是你在程序中聲明的一些參數,有固定的作用域。對於一個深度學習的網路來說,你首先需要構建出這個網路圖,然後創建一個會話,在會話中執行這個圖。

n

import tensorflow as tfnsess = tf.InteractiveSession() #創建會話nninit = tf.global_variables_initializer() #創建初始化的節點nsess.run(init) #執行這個節點的操作n

n

一些基本的操作就不說了,tf使用的版本是1.0,一些老版本的方法已經不一樣了,在這也做一點記錄。下面說一下tf里非常重要的變數作用域以及共享變數的問題。

n

2. 變數作用域和共享變數

n

為什麼需要使用變數作用域和共享變數呢?因為如果你不使用,每個變數都使用tf.Variable()創建,那麼對於一個有3層的神經網路,你就需要寫3個weights和3個biases,如果這個網路有100層,那你就需要寫100*2個變數。

n

看了不少論文的源碼,發現大家都使用了變數作用域的機制。tf中的這種機制主要由兩部分組成:

n

tf.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()方法來指定共享即可。

n

result1 = my_image_filter(image1)ntf.get_variable_scope().reuse_variables() #在作用域中共享變數nresult2 = my_image_filter(image2)n

n

如果想要深入理解這兩個方法是如何工作的,可以參考TensorFlow官方文檔中文版

n

我在寫代碼中遇到的一些問題,如下:

ValueError

這個問題造成的原因是,這個變數已經創建過了,並且我的代碼裡面沒有做任何共享變數的操作,所以就炸裂了,但是我在每個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,就共享一下變數,這樣就可以防止各種莫名原因導致的錯誤了。

n

3.模型的存儲

n

可以使用tf.train.Saver()來創建一個Saver管理模型中的變數

n

sess = 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

恢復變數

n

saver = tf.train.Saver()nsaver.restore(sess, "/tmp/model.ckpt") #需要存在同名的變數,不然需要在聲明saver是用一個dictionary設定n

n

下面是我自己的例子,前面已經保存過一個model.ckpt了,裡面的v1為全0

n

import 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之後讀取變數的例子:

n

import 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下
  • n

  • restore時,表示:checkpoint文件中key對應的值,應該restore到哪個variable
  • n

4.TensorBoard可視化

n

這部分內容,我也僅僅是嘗試了一下一些源碼,自己還沒寫過類似的。

n

d_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,如下圖所示:

scalar

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的。

n

G_sum = tf.summary.image("G", G)n

n

在tensorboard中顯示是這樣的:

此處輸入圖片的描述

tensorboard還可以生成整個計算圖,雖然我還不是很清楚怎麼讀這張圖

此處輸入圖片的描述

n

在代碼里,首先要用FileWriter創建一個file writer用來向硬碟寫summary數據

n

writer = 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了,命令是

n

tensorboard --logdir="logs/" #放在當前文件夾的logs文件夾下n

n

此處輸入圖片的描述

然後在瀏覽器中打開192.168.1.109:6006就好了


推薦閱讀:

TAG:TensorFlow | 深度学习DeepLearning | 人工智能 |