tensorflow動態設置trainable


tensorflow中定義的tf.Variable時,可以通過trainable屬性控制這個變量是否可以被優化器更新。但是,tf.Variable的trainable屬性是只讀的,我們無法動態更改這個只讀屬性。在定義tf.Variable時,如果指定trainable=True,那么會把這個Variable添加到“可被訓練的變量”集合中。

把trainable指定為布爾變量是不管用的,trainable只在定義變量的那一瞬間有用。

    # trainable只能是bool值,不能是張量
    trainable = tf.Variable(False, dtype=tf.bool)
    loss = tf.Variable(3.0, dtype=tf.float32, trainable=trainable)
    train_op = tf.train.AdamOptimizer(0.01).minimize(loss)
    with tf.Session()as sess:
        sess.run(tf.global_variables_initializer())
        for i in range(100):
            _, lo = sess.run([train_op, loss], feed_dict={
                trainable: i % 10 < 5
            })
            print('epoch', i, 'loss', lo)    

在定義Variable變量的那一瞬間,如果trainable=true,這個變量就會被添加到可被訓練的變量集合中去。當定義optimizer的minimize張量時,minimize張量就會讀取可被訓練的變量集合並構建張量。此后,即便可被訓練的變量集合發生改變,minimize張量也不會再去管哪些變量不能被訓練了。

    """
    如果optimizer的全部變量都是不可訓練的,tensorflow會拋出異常
    所以在這里使用兩個變量,兩個變量輪流變得可調節
    :return:
    """
    x = tf.Variable(3.0, dtype=tf.float32)
    y = tf.Variable(13.0, dtype=tf.float32)
    train_op = tf.train.AdamOptimizer(0.01).minimize(tf.abs(y - x))
    with tf.Session()as sess:
        sess.run(tf.global_variables_initializer())
        print("trainable_variables is a function")
        print(tf.trainable_variables, type(tf.trainable_variables()))
        print(tf.trainable_variables())
        print("tf.GraphKeys has several string key")
        print(tf.GraphKeys.TRAINABLE_VARIABLES, type(tf.GraphKeys.TRAINABLE_VARIABLES))
        print("tf.get_collection can get something by tf.GraphKeys")
        col = tf.get_collection_ref(tf.GraphKeys.TRAINABLE_VARIABLES)
        print(col, type(col))
        print("try remove x from trainable variables")
        del col[col.index(x)]  # 此處雖然可被訓練的變量集合變化了,但是train_op已經定義完了
        print(tf.trainable_variables())
        print('=======')
        for i in range(100):
            _, xx, yy = sess.run([train_op, x, y])
            print('epoch', i, xx, yy)  # 此處x和y都會變化


tf.GraphKeys

tf.GraphKeys中包含了所有默認集合的名稱,可以通過查看__dict__發現具體集合。

tf.GraphKeys.GLOBAL_VARIABLES:global_variables被收集在名為tf.GraphKeys.GLOBAL_VARIABLES的colletion中,包含了模型中的通用參數

tf.GraphKeys.TRAINABLE_VARIABLES:tf.Optimizer默認只優化tf.GraphKeys.TRAINABLE_VARIABLES中的變量。

  • tf.global_variables() GLOBAL_VARIABLES
    存儲和讀取checkpoints時,使用其中所有變量
    跨設備全局變量集合
  • tf.trainable_variables() TRAINABLE_VARIABLES
    訓練時,更新其中所有變量
    存儲需要訓練的模型參數的變量集合
  • tf.moving_average_variables() MOVING_AVERAGE_VARIABLES
    ExponentialMovingAverage對象會生成此類變量
    實用指數移動平均的變量集合
  • tf.local_variables() LOCAL_VARIABLES
    在global_variables()之外,需要用tf.init_local_variables()初始化
    進程內本地變量集合
  • tf.model_variables() MODEL_VARIABLES
    Key to collect model variables defined by layers.
    進程內存儲的模型參數的變量集合
  • QUEUE_RUNNERS 並非存儲variables,存儲處理輸入的QueueRunner
  • SUMMARIES 並非存儲variables,存儲日志生成相關張量

除了以上的函數外(上表中最后兩個集合並非變量集合,為了方便一並放在這里),還可以使用tf.get_collection(集合名)獲取集合中的變量,不過這個函數更多與tf.get_collection(集合名)搭配使用,操作自建集合。

Summary被收集在名為tf.GraphKeys.UMMARIES的colletion中,Summary是對網絡中Tensor取值進行監測的一種Operation,這些操作在圖中是“外圍”操作,不影響數據流本身,調用tf.scalar_summary系列函數時,就會向默認的collection中添加一個Operation。

我們也可以自定義變量集合、操作集合,這在正則化參數時非常有用。

x1 = tf.constant(1.0)
l1 = tf.nn.l2_loss(x1)
x2 = tf.constant([2.5, -0.3])
l2 = tf.nn.l2_loss(x2)
tf.add_to_collection("losses", l1)
tf.add_to_collection("losses", l2)
losses = tf.get_collection('losses')
loss_total = tf.add_n(losses)
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
losses_val = sess.run(losses)
loss_total_val = sess.run(loss_total)

我說

tensorflow臃腫龐雜,設計者的設計水平遠遠比不上keras。
tensorflow臃腫龐雜,做了許多外圍操作。比如為變量起名字,把變量添加到集合中,使用summary來監控訓練中產生的數據。這些操作都不是核心操作,分清核心操作和擴展操作非常重要。

  • 基本操作:如加減乘除、矩陣乘法等運算
  • python語言操作:基本上是一些外圍操作如collection,summary,dataset等。tf.gfile中定義了一堆文件操作,比python自帶的文件操作要高效易用。
  • 函數級封裝:把經常使用的基本操作定義成一個函數,如softmax、wx_b、cross_entropy等。
  • 層級封裝:定義一些常見層,如全連接層、卷積層等。
  • 模型封裝:keras中有Model,Tensorflow不好意思直接拿來用,起了個名叫“Estimator”。

optimizer其實也是一種封裝,optimizer其實就是對變量執行assign操作。除了使用反向傳播,我們也可以自己定義基於遺傳算法的optimizer。

攔截optimizer的梯度更新過程實現動態trainable

optimizer計算梯度的過程是應用梯度的過程是兩個步驟。計算梯度張量返回一個grad_and_vars列表,應用梯度需要grad_and_vars列表作為參數。

我們可以建立(loss,exemp)到minize張量的映射。

    # 攔截梯度更新過程
    class MyOptimizer:
        def __init__(self, optimizer: tf.train.Optimizer):
            self.optimizer = optimizer
            self.operations = dict()

        def minimize(self, loss, exemp):
            """
            注意:因為minimize操作是在sess運行時運行的,如果總是創建新操作,GPU內存會溢出
            """
            k = ' '.join(sorted([i.name for i in exemp])) + loss.name
            if k not in self.operations:
                a = [i for i in tf.trainable_variables() if i not in exemp]
                grad_vars = self.optimizer.compute_gradients(loss, a)
                op = self.optimizer.apply_gradients(grad_vars)
                self.operations[k] = op
            return self.operations[k]

    x = tf.Variable(3.0, dtype=tf.float32)
    y = tf.Variable(31.0, dtype=tf.float32)
    loss = tf.abs(x - y)
    """
    為了初始化optimizer中的一些信息,所以需要來一個加的operation形成一個張量
    """
    optimizer = MyOptimizer(tf.train.AdamOptimizer(0.01))
    train_op = optimizer.minimize(loss, [])
    with tf.Session()as sess:
        sess.run((tf.global_variables_initializer(), tf.local_variables_initializer()))
        for i in range(100):
            exemp = [x if i % 10 < 5 else y]
            _, xx, yy, lo = sess.run([optimizer.minimize(loss, exemp=exemp), x, y, loss])
            print('epoch', i, 'x', xx, 'y', yy, 'loss', lo)

這種方法的缺點在於使用loss和exemp作為key,如果key太多,定義的張量就會變多,這樣會產生很多變量。

嘗試優化一下,使用loss作為key。

        def __init__(self, optimizer: tf.train.Optimizer):
            self.optimizer = optimizer
            self.operations = dict()

        def minimize(self, loss, exemp):
            """
            注意:因為minimize操作是在sess運行時運行的,如果總是創建新操作,GPU內存會溢出
            """
            if loss.name not in self.operations:
                grad_vars = self.optimizer.compute_gradients(loss)
                self.operations[loss.name] = grad_vars
            grad_vars = self.operations[loss.name]
            exemp = set(exemp)
            grad_vars = list(filter(lambda x: x[1] not in exemp, grad_vars))
            op = self.optimizer.apply_gradients(grad_vars)
            return op

    x = tf.Variable(3.0, dtype=tf.float32)
    y = tf.Variable(31.0, dtype=tf.float32)
    loss = tf.abs(x - y)
    """
    為了初始化optimizer中的一些信息,所以需要來一個加的operation形成一個張量
    """
    optimizer = MyOptimizer(tf.train.AdamOptimizer(0.01))
    train_op = optimizer.minimize(loss, [])
    with tf.Session()as sess:
        sess.run((tf.global_variables_initializer(), tf.local_variables_initializer()))
        for i in range(100):
            exemp = [x if i % 10 < 5 else y]
            _, xx, yy, lo = sess.run([optimizer.minimize(loss, exemp=exemp), x, y, loss])
            print('epoch', i, 'x', xx, 'y', yy, 'loss', lo)

這種方法其實更差勁,因為apply_gradients依舊會創建許多張量(許多tf.assign_sub張量),而第一種方法反倒沒有那么多的張量。

梯度更新的過程其實就是一堆assign操作。

    # 攔截梯度更新過程
    class MyOptimizer:
        def __init__(self, optimizer: tf.train.Optimizer):
            self.optimizer = optimizer
            self.operations = dict()

        def minimize(self, loss, exemp):
            """
            注意:因為minimize操作是在sess運行時運行的,如果總是創建新操作,GPU內存會溢出
            """
            if loss.name not in self.operations:
                grad_vars = self.optimizer.compute_gradients(loss)
                op = [(variable, tf.assign_sub(variable, self.optimizer._lr * grad)) for grad, variable in grad_vars]
                self.operations[loss.name] = op
            grad_vars = self.operations[loss.name]
            op = [x[1] for x in grad_vars if x[0] not in exemp]
            return op

    x = tf.Variable(3.0, dtype=tf.float32)
    y = tf.Variable(31.0, dtype=tf.float32)
    loss = tf.abs(x - y)
    """
    為了初始化optimizer中的一些信息,所以需要來一個加的operation形成一個張量
    """
    optimizer = MyOptimizer(tf.train.AdamOptimizer(0.01))
    train_op = optimizer.minimize(loss, [])
    with tf.Session()as sess:
        sess.run((tf.global_variables_initializer(), tf.local_variables_initializer()))
        for i in range(100):
            exemp = [x if i % 10 < 5 else y]
            _, xx, yy, lo = sess.run([optimizer.minimize(loss, exemp=exemp), x, y, loss])
            print('epoch', i, 'x', xx, 'y', yy, 'loss', lo)

參考資料

https://www.cnblogs.com/hellcat/p/9006904.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM