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)