解决Keras与TensorFlow混编中trainable=False无效的问题
在Keras与TensorFlow混编场景下,直接设置模型层的trainable=False属性往往无法阻止优化器更新权重。本文通过引入tf.variable_scope区分变量作用域,利用tf.get_collection筛选特定作用域内的可训练变量,并在优化器中通过var_list指定更新范围,从而有效解决该兼容性问题。
问题背景
近期遇到一个典型的技术问题,具体现象如下:
假设我们已有一个预训练模型(如VGG16),需要对其进行修改,例如添加全连接层。出于某些限制,模型优化必须使用TensorFlow框架。TensorFlow的优化器默认会对tf.trainable_variables()返回的所有变量进行权重更新。问题在于,尽管我们将VGG16模型设置为trainable=False,但TensorFlow优化器依然对VGG16的权重进行了更新。
针对上述问题,经过查阅资料后找到了解决方案。下面我们将逐步还原整个问题的处理过程。
验证trainable=False无效
验证trainable=False无效的情况
首先导入预训练的VGG16模型,并将其设置为trainable=False。
from keras.applications import VGG16
import tensorflow as tf
from keras import layers
# 导入模型
base_mode = VGG16(include_top=False)
# 查看可训练的变量
tf.trainable_variables()
[<tf.Variable 'block1_conv1/kernel:0' shape=(3, 3, 3, 64) dtype=float32_ref>,
<tf.Variable 'block1_conv1/bias:0' shape=(64,) dtype=float32_ref>,
<tf.Variable 'block1_conv2/kernel:0' shape=(3, 3, 64, 64) dtype=float32_ref>,
<tf.Variable 'block1_conv2/bias:0' shape=(64,) dtype=float32_ref>,
<tf.Variable 'block2_conv1/kernel:0' shape=(3, 3, 64, 128) dtype=float32_ref>,
<tf.Variable 'block2_conv1/bias:0' shape=(128,) dtype=float32_ref>,
<tf.Variable 'block2_conv2/kernel:0' shape=(3, 3, 128, 128) dtype=float32_ref>,
<tf.Variable 'block2_conv2/bias:0' shape=(128,) dtype=float32_ref>,
<tf.Variable 'block3_conv1/kernel:0' shape=(3, 3, 128, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv1/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block3_conv2/kernel:0' shape=(3, 3, 256, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv2/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block3_conv3/kernel:0' shape=(3, 3, 256, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv3/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block4_conv1/kernel:0' shape=(3, 3, 256, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block4_conv2/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv2/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block4_conv3/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv3/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv2/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv2/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv3/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv3/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block1_conv1_1/kernel:0' shape=(3, 3, 3, 64) dtype=float32_ref>,
<tf.Variable 'block1_conv1_1/bias:0' shape=(64,) dtype=float32_ref>,
<tf.Variable 'block1_conv2_1/kernel:0' shape=(3, 3, 64, 64) dtype=float32_ref>,
<tf.Variable 'block1_conv2_1/bias:0' shape=(64,) dtype=float32_ref>,
<tf.Variable 'block2_conv1_1/kernel:0' shape=(3, 3, 64, 128) dtype=float32_ref>,
<tf.Variable 'block2_conv1_1/bias:0' shape=(128,) dtype=float32_ref>,
<tf.Variable 'block2_conv2_1/kernel:0' shape=(3, 3, 128, 128) dtype=float32_ref>,
<tf.Variable 'block2_conv2_1/bias:0' shape=(128,) dtype=float32_ref>,
<tf.Variable 'block3_conv1_1/kernel:0' shape=(3, 3, 128, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv1_1/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block3_conv2_1/kernel:0' shape=(3, 3, 256, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv2_1/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block3_conv3_1/kernel:0' shape=(3, 3, 256, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv3_1/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block4_conv1_1/kernel:0' shape=(3, 3, 256, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv1_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block4_conv2_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv2_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block4_conv3_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv3_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv1_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv1_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv2_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv2_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv3_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv3_1/bias:0' shape=(512,) dtype=float32_ref>]
# 设置 trainable=False
# base_mode.trainable = False似乎也是可以的
for layer in base_mode.layers:
layer.trainable = False
设置trainable=False后,再次检查可训练变量列表,发现列表内容与之前一致,说明设置并未生效。
# 再次查看可训练的变量 tf.trainable_variables()
[<tf.Variable 'block1_conv1/kernel:0' shape=(3, 3, 3, 64) dtype=float32_ref>,
<tf.Variable 'block1_conv1/bias:0' shape=(64,) dtype=float32_ref>,
<tf.Variable 'block1_conv2/kernel:0' shape=(3, 3, 64, 64) dtype=float32_ref>,
<tf.Variable 'block1_conv2/bias:0' shape=(64,) dtype=float32_ref>,
<tf.Variable 'block2_conv1/kernel:0' shape=(3, 3, 64, 128) dtype=float32_ref>,
<tf.Variable 'block2_conv1/bias:0' shape=(128,) dtype=float32_ref>,
<tf.Variable 'block2_conv2/kernel:0' shape=(3, 3, 128, 128) dtype=float32_ref>,
<tf.Variable 'block2_conv2/bias:0' shape=(128,) dtype=float32_ref>,
<tf.Variable 'block3_conv1/kernel:0' shape=(3, 3, 128, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv1/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block3_conv2/kernel:0' shape=(3, 3, 256, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv2/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block3_conv3/kernel:0' shape=(3, 3, 256, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv3/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block4_conv1/kernel:0' shape=(3, 3, 256, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block4_conv2/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv2/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block4_conv3/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv3/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv2/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv2/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv3/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv3/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block1_conv1_1/kernel:0' shape=(3, 3, 3, 64) dtype=float32_ref>,
<tf.Variable 'block1_conv1_1/bias:0' shape=(64,) dtype=float32_ref>,
<tf.Variable 'block1_conv2_1/kernel:0' shape=(3, 3, 64, 64) dtype=float32_ref>,
<tf.Variable 'block1_conv2_1/bias:0' shape=(64,) dtype=float32_ref>,
<tf.Variable 'block2_conv1_1/kernel:0' shape=(3, 3, 64, 128) dtype=float32_ref>,
<tf.Variable 'block2_conv1_1/bias:0' shape=(128,) dtype=float32_ref>,
<tf.Variable 'block2_conv2_1/kernel:0' shape=(3, 3, 128, 128) dtype=float32_ref>,
<tf.Variable 'block2_conv2_1/bias:0' shape=(128,) dtype=float32_ref>,
<tf.Variable 'block3_conv1_1/kernel:0' shape=(3, 3, 128, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv1_1/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block3_conv2_1/kernel:0' shape=(3, 3, 256, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv2_1/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block3_conv3_1/kernel:0' shape=(3, 3, 256, 256) dtype=float32_ref>,
<tf.Variable 'block3_conv3_1/bias:0' shape=(256,) dtype=float32_ref>,
<tf.Variable 'block4_conv1_1/kernel:0' shape=(3, 3, 256, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv1_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block4_conv2_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv2_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block4_conv3_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block4_conv3_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv1_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv1_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv2_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv2_1/bias:0' shape=(512,) dtype=float32_ref>,
<tf.Variable 'block5_conv3_1/kernel:0' shape=(3, 3, 512, 512) dtype=float32_ref>,
<tf.Variable 'block5_conv3_1/bias:0' shape=(512,) dtype=float32_ref>]
解决方案
解决方案
解决方法是在导入模型时建立variable_scope,将需要训练的变量置于另一个variable_scope中。随后通过tf.get_collection获取需要训练的变量集合,最后在TensorFlow优化器中使用var_list参数指定仅更新这些变量。
from keras import models
with tf.variable_scope('base_model'):
base_model = VGG16(include_top=False, input_shape=(224,224,3))
with tf.variable_scope('xxx'):
model = models.Sequential()
model.add(base_model)
model.add(layers.Flatten())
model.add(layers.Dense(10))
# 获取需要训练的变量
trainable_var = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, 'xxx')
trainable_var
[<tf.Variable 'xxx_2/dense_1/kernel:0' shape=(25088, 10) dtype=float32_ref>, <tf.Variable 'xxx_2/dense_1/bias:0' shape=(10,) dtype=float32_ref>]
# 定义tf优化器进行训练,这里假设有一个loss
loss = model.output / 2; # 随便定义的,方便演示
train_step = tf.train.AdamOptimizer().minimize(loss, var_list=trainable_var)
总结
总结
在Keras与TensorFlow混编环境中,Keras中设置的trainable=False属性对TensorFlow层面的变量管理不起作用。
解决方法是通过variable_scope对变量进行隔离区分,利用tf.get_collection提取目标作用域下的可训练变量,并在TensorFlow优化器中通过var_list明确指定参与训练的变量列表。
以上关于解决Keras与TensorFlow混编中trainable=False设置无效问题的内容分享完毕,希望能为读者提供参考。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













