keras 中 reuse 问题

  • 结论:在搭建GAN判别器时,慎用keras。
  • 原因:tf.variable_scope()  的 reuse=True 对keras变量不起作用,导致判别real_img的判别器与判别fake_img的判别器是两个独立的判别器,使对抗训练失效。
  • 导致现象:real_img 的判别结果快速收敛到1, fake_img的判别结果快速收敛到0, 且不再变化, 快速收敛是指在100~1000次迭代内完全收敛,不再变化。当判别器损失函数中用到log函数时, 损失函数会出现 nan 现象。
  • 其他: 无论是用keras.layers 还是tensorflow.keras.layers都存在这个问题。具体在keras中如何使用变量重用,我还没有探究,希望有看到的可以指教。

-------------------------------------------------------------------------------------------------------------------------------------------------

个人经历:

        我之前在搭建神经网络时,用的卷积层是定义在tf.layers中的:

from tensorflow.compat.v1.layers import Conv2D

        最近,发现keras 很好用,尤其对LSTM的封装很好用,所以开始转用keras:

from keras.layers import Conv2D

        一直用的挺顺利的,直到这两天在训练GAN时出现问题:判别器对fake_img的判别分值很快就趋近于0,对real_img的判别分值很快就趋近于1。

        我百思不得其解,调了学习率,多次检查了GAN的损失函数,都没有问题,折腾了一天时间。直到刚刚突然想到变量重用的问题,检查了变量空间,发现以下问题:用keras的Conv2D构建的判别器,在计算real_map与fake_map时,重复创建了判别器的计算节点,并没有执行变量重用,示例代码如下:

import numpy as np
import tensorflow.compat.v1 as tf
from keras.layers import Conv2D
from tensorflow.keras.layers import Conv2D

inputs = tf.convert_to_tensor(np.random.random((1,256,256,3)).astype(np.float32))

with tf.variable_scope("dis"):
    conv1 = Conv2D(32,(3,3),strides=(1,1),padding="same",activation='relu',name="conv1")(inputs)
    
with tf.variable_scope("dis",reuse=True):
    conv2 = Conv2D(32, (3, 3), strides=(1, 1), padding="same", activation='relu', name="conv1")(inputs)

var_list = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
for v in var_list:
    print(v.name)

"""
返回:
dis/conv1/kernel:0
dis/conv1/bias:0
dis_1/conv1/kernel:0
dis_1/conv1/bias:0
"""

        由代码可见,我在命名空间中设置了 reuse=True, 但是代码仍旧重复创建了相同的计算节点。当在训练GAN时遇到这个问题时,就会导致 计算real_map的判别器与计算fake_map的判别器是两个独立的判别器,他们两个完全不相关,所以就达不到对抗训练的目的了。

        很明显,这个问题是不应该出现的,tensorflow设置了reuse参数,应该是要起作用的,于是我又测试了tf.layer.Conv2D的情况,发现用tf.layer.Conv2D 不存在这个问题,示例代码如下(仅仅改变了Conv2D的来源):

import numpy as np
import tensorflow.compat.v1 as tf
# from keras.layers import Conv2D
from tensorflow.compat.v1.layers import Conv2D

inputs = tf.convert_to_tensor(np.random.random((1,256,256,3)).astype(np.float32))

with tf.variable_scope("dis"):
    conv1 = Conv2D(32,(3,3),strides=(1,1),padding="same",activation='relu',name="conv1")(inputs)

with tf.variable_scope("dis",reuse=True):
    conv2 = Conv2D(32, (3, 3), strides=(1, 1), padding="same", activation='relu', name="conv1")(inputs)

var_list = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
for v in var_list:
    print(v.name)
"""
返回:
dis/conv1/kernel:0
dis/conv1/bias:0
"""

        可以发现,当改用 tf.compat.v1.layers 的源时不存在这个问题。

        另外, 在reuse = True的命名空间中,只能重复使用已经在该空间中定义的变量,不能创建新的变量,否则会报错。

        如果,既希望能够变量重用,又不耽误创建新的变量,请用 reuse=tf.AUTO_REUSE,示例代码如下(与上例相比增加了一个新变量):

import numpy as np
import tensorflow.compat.v1 as tf
# from keras.layers import Conv2D
from tensorflow.compat.v1.layers import Conv2D

inputs = tf.convert_to_tensor(np.random.random((1,256,256,3)).astype(np.float32))

with tf.variable_scope("dis"):
    conv1 = Conv2D(32,(3,3),strides=(1,1),padding="same",activation='relu',name="conv1")(inputs)

with tf.variable_scope("dis",reuse=True):
    conv2 = Conv2D(32, (3, 3), strides=(1, 1), padding="same", activation='relu', name="conv1")(inputs)
    conv3 = Conv2D(32, (3, 3), strides=(1, 1), padding="same", activation='relu', name="conv2")(inputs)

var_list = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
for v in var_list:
    print(v.name)
"""
报错:
ValueError: Variable dis/conv2/kernel does not exist, or was not created with tf.get_variable(). Did you mean to set reuse=tf.AUTO_REUSE in VarScope?
"""

当把 reuse=True 改为 reuse=tf.AUTO_REUSE后,错误消失:

import numpy as np
import tensorflow.compat.v1 as tf
# from keras.layers import Conv2D
from tensorflow.compat.v1.layers import Conv2D

inputs = tf.convert_to_tensor(np.random.random((1,256,256,3)).astype(np.float32))

with tf.variable_scope("dis"):
    conv1 = Conv2D(32,(3,3),strides=(1,1),padding="same",activation='relu',name="conv1")(inputs)

with tf.variable_scope("dis",reuse=tf.AUTO_REUSE):
    conv2 = Conv2D(32, (3, 3), strides=(1, 1), padding="same", activation='relu', name="conv1")(inputs)
    conv3 = Conv2D(32, (3, 3), strides=(1, 1), padding="same", activation='relu', name="conv2")(inputs)

var_list = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES)
for v in var_list:
    print(v.name)
"""
返回:
dis/conv1/kernel:0
dis/conv1/bias:0
dis/conv2/kernel:0
dis/conv2/bias:0
"""

        可见,在已有的变量命名空间中新定义的变量成功了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值