tensorflow中定義的tf.Variable時,能夠經過trainable屬性控制這個變量是否能夠被優化器更新。可是,tf.Variable的trainable屬性是隻讀的,咱們沒法動態更改這個只讀屬性。在定義tf.Variable時,若是指定trainable=True,那麼會把這個Variable添加到「可被訓練的變量」集合中。html
把trainable指定爲布爾變量是無論用的,trainable只在定義變量的那一瞬間有用。python
# 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中,包含了模型中的通用參數app
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。spa
咱們也能夠自定義變量集合、操做集合,這在正則化參數時很是有用。scala
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)