Tensorflow[基礎篇]——變量范圍,共享變量

前言

本文章是一篇源于Tensorflow里面Programmer's Guide的Sharing Variables教程。這也算是自己在學習tensorflow里面的一些感悟吧,所以就記錄下來與大家分享并作為回憶錄。在tensorflow官網的地址:Sharing Variables。這個變量范圍和共享變量一開始看起來好像沒啥用,可是一旦你在實際項目進行deep learning編碼的時候,你會發現這是一個對代碼重構和編碼優化很有幫助的東西。過幾天我會寫一篇使用CNN模型進行人臉識別的實戰文章,里面很能體現今天講的內容。


問題

先看一個例子(也是官網上的例子)

def my_image_filter(input_images):
    conv1_weights = tf.Variable(tf.random_normal([5, 5, 32, 32]),
        name="conv1_weights")
    conv1_biases = tf.Variable(tf.zeros([32]), name="conv1_biases")
    conv1 = tf.nn.conv2d(input_images, conv1_weights,
        strides=[1, 1, 1, 1], padding='SAME')
    relu1 = tf.nn.relu(conv1 + conv1_biases)

    conv2_weights = tf.Variable(tf.random_normal([5, 5, 32, 32]),
        name="conv2_weights")
    conv2_biases = tf.Variable(tf.zeros([32]), name="conv2_biases")
    conv2 = tf.nn.conv2d(relu1, conv2_weights,
        strides=[1, 1, 1, 1], padding='SAME')
    return tf.nn.relu(conv2 + conv2_biases)

在官網上說想利用這個方法對兩張圖片進行相同的卷積操作。但是Variable()會重新創建變量,這樣變量就不能復用了。這是個很蛋疼的事情。

# 第一次執行方法創建4個變量
result1 = my_image_filter(image1)
# 第二次執行再創建4個變量
result2 = my_image_filter(image2)

但我覺得還有個問題,就是代碼的重復問題。盡管這個問題不是解決的重點,但作為寫慣項目的人來說,這個真的受不了。Tensorflow給出一個很優雅的解決方案。


引入Variable Scope(變量范圍)

首先這里要介紹兩個方法:

  • tf.get_variable(<name>, <shape>, <initializer>) :創建一個名為<name>的變量
  • tf.variable_scope(<scope_name>):創建namespaces

其中創建了variable_scope(<scope_name>)后,get_variable(<name>)的變量名就會變為scope_name/name啦!這就可以用來管理我們的變量啦!因為我們有時候在不同的情況想創建相同名稱的變量,假如用get_variable()創建兩個相同名字的變量是會報錯的,但你用Variable()的話就會把之前相同名稱的變量給覆蓋了。所以我們就用了variable_scope()這個方法了。話不多說,直接貼代碼展示一下。

# -*- coding: utf-8 -*-

import tensorflow as tf
import numpy as np

input_images = tf.placeholder(tf.float32, shape=(1, 32, 32, 1))

# 定義了一層卷積神經網絡
def conv_relu(input, kernel_shape, bias_shape):
    # 創建名為weights的變量
    weights = tf.get_variable("weights", kernel_shape, initializer=tf.random_normal_initializer())
    # 創建名為biases的變量
    biases = tf.get_variable("biases", bias_shape, initializer=tf.constant_initializer(0.0))

    conv = tf.nn.conv2d(input, weights, strides=[1, 1, 1, 1], padding='SAME')

    return tf.nn.relu(conv + biases)

def my_image_filter(input_images):
    with tf.variable_scope("conv1"):
        # 在名為conv1的variable scope下調用一層神經網絡,對應的參數名為
        # "conv1/weights", "conv1/biases"
        relu1 = conv_relu(input_images, [3, 3, 1, 1], [1])
    with tf.variable_scope("conv2"):
        # 在名為conv2的variable scope下調用一層神經網絡,對應的參數名為
        # "conv2/weights", "conv2/biases"
        return conv_relu(relu1, [3, 3, 1, 1], [1])

with tf.variable_scope("image_filter") as scope:
    result1 = my_image_filter(input_images)
    # 重用變量
    scope.reuse_variables()
    result2 = my_image_filter(input_images)

init = tf.global_variables_initializer();

with tf.Session() as sess:
    sess.run(init)
    image = np.random.rand(1, 32, 32, 1)
    result1 = sess.run(result1, feed_dict={input_images: image})
    result2 = sess.run(result2, feed_dict={input_images: image})

    print(result2.all() == result1.all())

理解get_variable_scope()

其實根據上面的代碼,我們可以想到一些關于這個內容。假如tf.get_variable_scope().reuse == False的話,在這個scope下面的變量都會創建新的變量,加入有同樣名字的話就拋出異常。所以想重用變量的話,就要變成reuse設置為True啦。

with tf.variable_scope("foo"):
    v = tf.get_variable("v", [1])
with tf.variable_scope("foo", reuse=True):
    v1 = tf.get_variable("v", [1])
assert v1 is v
# True

理解variable_scope

1. scope可以一層一層的疊下去,如

with tf.variable_scope("foo"):
    with tf.variable_scope("bar"):
        v = tf.get_variable("v", [1])
assert v.name == "foo/bar/v:0"

2. 同一個scope里面調用同名變量名則:

with tf.variable_scope("foo"):
    v = tf.get_variable("v", [1])
    tf.get_variable_scope().reuse_variables()
    v1 = tf.get_variable("v", [1])
assert v1 is v

3. scope的reuse可繼承,子層的可重用性不影響父層的可重用性:

with tf.variable_scope("root"):
    # 一開始root scope是不可重用的
    assert tf.get_variable_scope().reuse == False
    with tf.variable_scope("foo"):
        # 接下來root的子scope foo也是不可重用的
        assert tf.get_variable_scope().reuse == False
    with tf.variable_scope("foo", reuse=True):
        # 這里我們設置foo可重用
        assert tf.get_variable_scope().reuse == True
        with tf.variable_scope("bar"):
            # 這樣他的的子scope就繼承了父scope的可復用性
            assert tf.get_variable_scope().reuse == True
    # 退回到root scope,發現并沒有影響到
    assert tf.get_variable_scope().reuse == False

4. 獲取variable scope

我們調用variable_scope創建新的scope后,可能經過一段時間又會再度使用這個scope。那就會用到像下面的代碼那樣啦

with tf.variable_scope("foo") as foo_scope:
    v = tf.get_variable("v", [1])

# 進行其他操作....

with tf.variable_scope(foo_scope):
    w = tf.get_variable("w", [1])
with tf.variable_scope(foo_scope, reuse=True):
    v1 = tf.get_variable("v", [1])
    w1 = tf.get_variable("w", [1])
assert v1 is v
assert w1 is w

而且可以在任何時候都可以跳回原來的scope,并且獨立于當前所在的scope:

with tf.variable_scope("foo") as foo_scope:
    assert foo_scope.name == "foo"
with tf.variable_scope("bar"):
    with tf.variable_scope("baz") as other_scope:
        assert other_scope.name == "bar/baz"
        with tf.variable_scope(foo_scope) as foo_scope2:
            assert foo_scope2.name == "foo"  # 沒有任何改變

本人認為這個用法很容易把自己思維混淆,建議編程的時候還是注意點或者少用。

5. 變量作用域中的初始化器

在我們調用tf.get_variable()真的十分蛋疼,因為都需要定義initializer,因為有時候我們都使用一樣的初始器。那variable_scope就幫到你了。我們可以在variable_scope方法里面傳入參數initializer作為當前scope下每個變量的默認初始器

with tf.variable_scope("foo", initializer=tf.constant_initializer(0.4)):
    v = tf.get_variable("v", [1])
    assert v.eval() == 0.4  # 默認初始器起作用了
    w = tf.get_variable("w", [1], initializer=tf.constant_initializer(0.3)):
    assert w.eval() == 0.3  # 這個變量我定義了其他初始器
    with tf.variable_scope("bar"):
        v = tf.get_variable("v", [1])
        assert v.eval() == 0.4  # 原來子scope也會繼承父scope的初始器,這個和reuse有點相似哦。

好啦!今天的內容就到這里為止啦!是不是很簡單呢?哈哈。看完記得早睡哦~早唞!好夢!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內容