运行Tensorflow 2.0代码时出现“ValueError: tf.function-decorated function tried to create variables on non-first call”错误。我做错了什么?

46

error_giving_notebook

non_problematic_notebook

可以看到,在“error_giving_notebook”中使用了tf.function修饰符,它会抛出ValueError错误,而仅仅移除tf.function修饰符的同一笔记本在“non_problematic_notebook”中却可以正常运行。可能的原因是什么?


2
如果看起来你正在多次调用函数,并且这些函数试图在它们应该仅在第一次调用时创建新变量的情况下创建新变量?顺便说一下,我从未在训练循环中使用过@tf.function,你想使用它有特殊的原因吗? - Daniel Möller
https://www.tensorflow.org/tutorials/customization/performance#variables --- 我不确定在这些函数内部创建了一个新的变量,但也许是梯度带在做这件事情。 - Daniel Möller
重新考虑后,我认为训练的图形版本应该使用tf.gradients而不是梯度磁带。但是,要使tf.gradients起作用,整个模型从开始到结束都必须是一个图形。(在您的情况下似乎没问题)。现在,如果您的代码只是笔记本中的内容,您可能真的应该考虑使用回调的model.fit()而不是自定义训练循环。 - Daniel Möller
3个回答

98

如果你正在尝试在 TensorFlow 2.0 中使用函数装饰器,请在导入 TensorFlow 后使用以下行使函数立即运行:

tf.config.experimental_run_functions_eagerly(True)

由于上述内容已经被弃用(不再是实验性的?),请使用以下内容代替:

tf.config.run_functions_eagerly(True)

如果你想了解更多,请参考此链接


1
我实际上是在使用一个共享层,导致了错误。这让我免于疯狂,谢谢! - Lamberto Basti
这个错误可能会在使用Keras时发生。为了使用这个解决方案,只需执行 import tensorflow as tf 然后执行 tf.config... - YScharf
1
请查看此链接:link - Singh
那么解决方案会损失90%的TF性能吗? - Alberto Sinigaglia

12

这里的问题在于 conv2d 类的 call 方法的返回值:

if self.bias:
  if self.pad == 'REFLECT':
    self.p = (self.filter_size - 1) // 2
    self.x = tf.pad(inputs, [[0, 0], [self.p, self.p], [self.p, self.p], [0, 0]], 'REFLECT')
    return Conv2D(filters=self.filter_num, kernel_size=(self.filter_size, self.filter_size), strides=(self.stride, self.stride),
                                  padding='VALID', use_bias=True, kernel_initializer=self.w, bias_initializer=self.b)(self.x)
  else:
    return Conv2D(filters=self.filter_num, kernel_size=(self.filter_size, self.filter_size), strides=(self.stride, self.stride),
                                  padding=self.pad, use_bias=True, kernel_initializer=self.w, bias_initializer=self.b)(inputs)
else:
   if self.pad == 'REFLECT':
      self.p = (self.filter_size - 1) // 2
      self.x = tf.pad(inputs, [[0, 0], [self.p, self.p], [self.p, self.p], [0, 0]], 'REFLECT')
      return Conv2D(filters=self.filter_num, kernel_size=(self.filter_size, self.filter_size), strides=(self.stride, self.stride),
                                  padding='VALID', use_bias=False, kernel_initializer=self.w)(self.x)
   else:
      return Conv2D(filters=self.filter_num, kernel_size=(self.filter_size, self.filter_size), strides=(self.stride, self.stride),
                                  padding=self.pad, use_bias=False, kernel_initializer=self.w)(inputs)
每次调用时,返回一个Conv2D对象会创建tf.Variable(s) (卷积层的权重和偏置)。
predictions = model(images)

在您的tf装饰函数中,因此出现了异常。

解决此问题的一种可能的方法是更改conv2d类中的构建和调用方法,如下所示:

def build(self, inputs):
  self.w = tf.random_normal_initializer(mean=0.0, stddev=1e-4)
  if self.bias:
    self.b = tf.constant_initializer(0.0)
  else:
    self.b = None

  self.conv_a = Conv2D(filters=self.filter_num, kernel_size=(self.filter_size, self.filter_size), strides=(self.stride, self.stride), padding='VALID', use_bias=True, kernel_initializer=self.w, bias_initializer=self.b)
  self.conv_b = Conv2D(filters=self.filter_num, kernel_size=(self.filter_size, self.filter_size), strides=(self.stride, self.stride), padding=self.pad, use_bias=True, kernel_initializer=self.w, bias_initializer=self.b)
  self.conv_c = Conv2D(filters=self.filter_num, kernel_size=(self.filter_size, self.filter_size), strides=(self.stride, self.stride), padding='VALID', use_bias=False, kernel_initializer=self.w)
  self.conv_d = Conv2D(filters=self.filter_num, kernel_size=(self.filter_size, self.filter_size), strides=(self.stride, self.stride),padding=self.pad, use_bias=False, kernel_initializer=self.w)  

def call(self, inputs):
  if self.bias:
    if self.pad == 'REFLECT':
      self.p = (self.filter_size - 1) // 2
      self.x = tf.pad(inputs, [[0, 0], [self.p, self.p], [self.p, self.p], [0, 0]], 'REFLECT')
      return self.conv_a(self.x)
    else:
      return self.conv_b(inputs)
  else:
     if self.pad == 'REFLECT':
        self.p = (self.filter_size - 1) // 2
        self.x = tf.pad(inputs, [[0, 0], [self.p, self.p], [self.p, self.p], [0, 0]], 'REFLECT')
        return self.conv_c(self.x)
     else:
        return self.conv_d(inputs)

为了更好地理解AutoGraph和@tf.function的工作原理,我建议看一下这个链接


3
请在导入TensorFlow后使用以下代码行,以启用即时运行功能,当您尝试在TF 2.0中使用函数修饰符时:
import tensorflow as tf
tf.config.run_functions_eagerly(True)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接