TensorFlow——共享變數的使用方法

Lily發表於2019-05-27

1.共享變數用途

在構建模型時,需要使用tf.Variable來建立一個變數(也可以理解成節點)。當兩個模型一起訓練時,一個模型需要使用其他模型建立的變數,比如,對抗網路中的生成器和判別器。如果使用tf.Variable,將會生成一個新的變數,而我們需要使用原來的那個變數。這時就是通過引入get_Variable方法,實現共享變數來解決這個問題。這種方法可以使用多套網路模型來訓練一套權重。

2.使用get_Variable獲取變數

get_Variable一般會配合Variable_scope一起使用,以實現共享變數。Variable_scope的含義是變數作用域。在某一作用域中的變數可以被設定成共享的方式,被其他網路模型使用。

get_Variable函式的定義如下:

tf.get_Variable(<name>, <shape>, <initializer>)

在TensorFlow裡,使用get_Variable時候生成的變數是以指定的name屬性為唯一標識,並不是定義的變數名稱。使用時一般是通過name屬性定位到具體變數,並將其共享到其他的模型中。

import tensorflow as tf
import numpy as np


var1 = tf.Variable(1.0, name='first_var')
print("var1: ", var1.name)

var1 = tf.Variable(2.0, name='first_var')
print('var1: ', var1.name)

var2 = tf.Variable(3.0)
print('var2: ', var2.name)

var2 = tf.Variable(4.0)
print('var1: ', var2.name)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print("var1=", var1.eval())
    print("var2=", var2.eval())
    print()

在上述的程式碼中,,可以看到記憶體中有兩個var1,並且他們的name是不一樣的,對於圖來說,後面的var1是生效的。當Variable定義沒有指定名字時,系統會自動的加上一個名字Variable:0

3.get_Variable用法演示

import tensorflow as tf
import numpy as np


get_var1 = tf.get_variable('firat_var_1', [1], initializer=tf.constant_initializer(2))
print("var1: ", get_var1.name)

get_var1 = tf.get_variable('firat_var_2', [1], initializer=tf.constant_initializer(3))
print("var1: ", get_var1.name)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print("var1=", get_var1.name)
    print("var1=", get_var1.eval())

使用不同的name定義變數,當使用相同的name時,會丟擲異常,變數名可以相同,但是name是不能相同的。

如果要使用相同的name的話,我們需要使用variable_scope將他們隔開,看如下程式碼:

import tensorflow as tf

with tf.variable_scope('test_1'):
    var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)

with tf.variable_scope('test_2'):
    var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)

print("var1:", var1.name)
print("var1:", var1.name)

根據程式的執行結果,我們可以發現變數的名字加上了作用域的名稱,這樣使得我們能夠在不同的作用域下面定義name相同的變數,同時,scope還支援巢狀定義,

with tf.variable_scope('test_0'):
    with tf.variable_scope('test_1'):
        var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)

    with tf.variable_scope('test_2'):
        var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)

print("var1:", var1.name)
print("var1:", var1.name)

4.共享作用域

使用作用域中的引數reuse可以實現共享變數功能

在variable_scope裡面有一個reuse=True屬性,表示使用已經定義過的變數,這時,get_variable將不會在建立新的變數,而是去圖中get_variable所建立的變數中找與name相同的變數。

import tensorflow as tf

with tf.variable_scope('test_0'):
    var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
    with tf.variable_scope('test_2'):
        var2 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)

with tf.variable_scope('test_0', reuse=True):
    var3 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
    with tf.variable_scope('test_2'):
        var4 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)


print("var1:", var1.name)
print("var2:", var2.name)

print("var3:", var3.name)
print("var4:", var4.name)

在上述的輸出結果中,我們可以看到,var1和var3的名字一樣,var2和var4的名字一樣,則表明他們是同一個變數,如此就實現了變數的共享。在實際應用中,可以將1,2和3,4分別放在不同的模型進行訓練,但是他們會作用於同一個模型的學習引數上。

使用anaconda的spyder工具執行時,程式碼只能執行一次,第二次執行將會報錯。可以退出當前的kernel,再重新進入一下,因為tf.get_varibale在建立變數時,會去檢查圖中是否已經建立過該變數,如果建立過且不是共享的方式,則會報錯。

因而可以使用tf.reset_default_graph(),將圖裡面的變數清空,就可以解決這個問題。

5.初始化共享變數

variable_scope和get_variable都具有初始化的功能。在初始化時,如果沒有對當前變數初始化,則TensorFlow會預設使用作用域的初始化,並且作用域的初始化方法也有繼承功能。

import tensorflow as tf

with tf.variable_scope('test_0', initializer=tf.constant_initializer(0.15)):
    var1 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
    with tf.variable_scope('test_2'):
        var2 = tf.get_variable('first_var', shape=[2], dtype=tf.float32)
        var3 = tf.get_variable('first_var_2', shape=[2], initializer=tf.constant_initializer(0.315))

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print("var1:", var1.eval())
    print("var2:", var2.eval())
    print("var3:", var3.eval())

當變數沒有進行初始化時,會繼承它的域的初始化方式,域也會繼承它的上一級的域的初始化方式。在多模型訓練時,常常可以對模型中的張量進行分割槽,同時,同一進行初始化。在變數共享方面,可以使用tf.AUTO_REUSE來為reuse屬性賦值。tf。AUTO_REUSE可以實現第一次呼叫variable_scope時,傳入reuse的值為false,再次呼叫時,reuse的值為True。

6.作用域與操作符的受限範圍

variable_scope還可以使用with variable_scope as xxxscope的方式定義作用域,當使用這種方式時,將不會在受到外層的scope所限制。

import tensorflow as tf

with tf.variable_scope('test2', initializer=tf.constant_initializer(1.5)) as sp:
    var1 = tf.get_variable('var1', [2], dtype=tf.float32)

print(sp.name)
print(var1.name)

with tf.variable_scope('test1', dtype=tf.float32, initializer=tf.constant_initializer(5.5)):
    var2 = tf.get_variable('var1', [2], dtype=tf.float32)
    with tf.variable_scope(sp) as sp1:
        var3 = tf.get_variable('var2', [2], dtype=tf.float32)

print("var2:", var2.name)


print("var3:", var3.name)


with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print("var2:", var2.eval())
    print("var2:", var3.eval())

通過with tf.variable_scope(sp) as sp1我們可知其沒有收到外層的作用域所限制,初始化的操作時,它的值不是外層作用域的初始化值,而是指定的作用域的初始化的值。

對於操作符而言,不僅收到tf.name_scope限制還收到tf.variable_scope限制。

import tensorflow as tf
import numpy as np

with tf.variable_scope('scope'):
    with tf.name_scope('op'):
        v = tf.get_variable('var1', [1])
        x = v + 2.0

print("v:", v.name)
print('x', x.name)

根據結果,我們可知,通過新增tf.name_scope('op'):作用域時,變數的命名並沒有收到限制,只是改變了op的命名,通過tf.name_scope(''):還可以返回到頂層的作用域中。

import tensorflow as tf
import numpy as np

with tf.variable_scope('scope'):
    var1 = tf.get_variable("v", [1])

    with tf.variable_scope('scope_1'):
        var2 = tf.get_variable("v", [1])

        with tf.name_scope(''):
            var3 = tf.get_variable('var1', [1])
            x = var3 + 2.0

print("var1 ", var1.name)
print('var2 ', var2.name)
print('var3 ', var3.name)
print('x ', x.name)

通過將通過tf.name_scope('')設定為空,對於變數名是沒有影響,但是可以看到x的命名,它已經變成了最外層的命名了。

 

相關文章