Tensorflow保存/恢复批量归一化

7

我在Tensorflow中使用批量标准化训练了一个模型。我想保存这个模型并在以后进行恢复使用。批量标准化是通过

def batch_norm(input, phase):
    return tf.layers.batch_normalization(input, training=phase)

在训练期间,阶段为 True ,而在测试期间为 False

似乎只需调用

saver = tf.train.Saver()
saver.save(sess, savedir + "ckpt")

无法很好地工作,因为当我恢复模型时,它首先会显示成功恢复。如果我只运行图中的一个节点,则还会显示 Attempting to use uninitialized value batch_normalization_585/beta。这与未正确保存模型有关还是我错过了其他东西?


可能与恢复和保存的模型批归一化层的命名不匹配有关。 - Ishant Mrinal
有没有可能修复这个问题? - ALeex
谢谢你的帮助!在问一个小问题之后,我会尝试这个方法:我需要像原始训练图一样定义图形吗?还是我只需要打开一个会话,导入操作将为我重新组织图形? - ALeex
你可以阅读tf.import_meta_graph的文档,这里需要你的checkpoint的.meta文件作为输入,然后只需使用checkpoint文件还原会话。 - Ishant Mrinal
tf.import_meta_graph 已经卡了将近30分钟了。我按照文档做了,但似乎错过了什么东西。 - ALeex
显示剩余3条评论
2个回答

8
我也遇到了“尝试使用未初始化值batch_normalization_585/beta”的错误。这是由于声明保存器时,使用空括号的原因,就像这样:
         saver = tf.train.Saver() 

该保存器将保存包含在tf.trainable_variables()中但不包含批量归一化移动平均值的变量。要在保存的ckpt中包含这些变量,您需要执行以下操作:

         saver = tf.train.Saver(tf.global_variables())

这会保存所有的变量,因此内存占用非常大。或者你必须识别具有移动平均或方差的变量,并通过声明它们来保存它们,例如:

         saver = tf.train.Saver(tf.trainable_variables() + list_of_extra_variables)

1
如果您想要恢复移动平均值,如果您明确提到需要将什么放入“list_of_extra_variables”中,这将非常有帮助。 - Eric Auld
如果您的批量归一化层是 bn,那么我相信它应该是 bn.moving_meanbn.moving_variance - Jon Deaton

4

如果需要解释的话,就是每当你在TensorFlow中创建一个操作时,都会向图中添加一个新节点。在同一张图中,没有两个节点可以拥有相同的名称。你可以为任何你创建的节点定义名称,但如果你不指定名称,TensorFlow会以确定性的方式(即不随机,而是始终具有相同的顺序)为你选择一个名称。如果你添加两个数字,它可能是Add,但如果你进行另一个加法运算,由于没有两个节点可以拥有相同的名称,它可能是像Add_2这样的东西。一旦在图中创建了一个节点,其名称就无法更改。许多函数会依次创建几个子节点;例如,tf.layers.batch_normalization会创建一些内部变量betagamma

保存和恢复的工作方式如下:

  1. 创建代表所需模型的图。该图包含将由saver保存的变量。
  2. 初始化、训练或执行其他操作,模型中的变量被分配一些值。
  3. 调用saver上的save方法,将变量的值保存到文件中。
  4. 现在,在不同的图中重新创建模型(它可以是完全不同的Python会话,也可以是与第一个图共存的另一个图)。模型必须以与第一个相同的方式创建。
  5. 调用saver上的restore方法,检索变量的值。

为了使这个过程工作,第一张图和第二张图中变量的名称必须完全相同

在你的例子中,TensorFlow抱怨变量batch_normalization_585/beta。看起来你已经在同一张图中调用了tf.layers.batch_normalization将近600次,因此你有那么多的beta变量挂在那里。我怀疑你实际上不需要那么多,所以我猜你只是在尝试API并最终得到了那么多的副本。

这是一个应该可以工作的草稿:

import tensorflow as tf

def make_model():
    input = tf.placeholder(...)
    phase = tf.placeholder(...)
    input_norm = tf.layers.batch_normalization(input, training=phase))
    # Do some operations with input_norm
    output = ...
    saver = tf.train.Saver()
    return input, output, phase, saver

# We work with one graph first
g1 = tf.Graph()
with g1.as_default():
    input, output, phase, saver = make_model()
    with tf.Session() as sess:
        # Do your training or whatever...
        saver.save(sess, savedir + "ckpt")

# We work with a second different graph now
g2 = tf.Graph()
with g2.as_default():
    input, output, phase, saver = make_model()
    with tf.Session() as sess:
        saver.restore(sess, savedir + "ckpt")
        # Continue using your model...

通常情况下不是将两个图并排放置,而是在另一个 Python 会话中重新创建相同的图。最重要的是在两种情况下以相同的方式(因此使用相同的节点名称)创建模型。


谢谢你的回答!我的做法是在另一个脚本中打开一个会话来恢复相同图形下的变量,我认为这应该足以重新定义图形并恢复变量。我之前用另一个模型做过这个,效果很好,所以我仍然不知道为什么现在不起作用。此外,我发现我已经调用了将近600次tf.layers.batch_normalization,因为我只想要大约150个。 - ALeex
我尝试了你的实现,但我的版本出现了故障。基本上,我的输入是通过管道完成的,而不是使用占位符来提供数据。我将整个管道包含到制作模型的函数中,但它给了我一个 ValueError: Tensor("random_shuffle_queue_EnqueueMany/component_1:0", shape=(80000, 300), dtype=float32) must be from the same graph as Tensor("random_shuffle_queue:0", shape=(), dtype=resource). 的错误信息。请帮我解决这个问题。 - ALeex
@ALeex 我在输入管道方面没有太多经验,但是像错误消息所说的那样,你正在尝试在不同的图中使用一个在一个图中创建的对象,这是不可能的。模型中的所有对象都必须在同一个图中创建(默认图或者手动使用 tf.Graph 创建的图)。 - jdehesa

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