【问题标题】:Is there a way to automate transfer learning with Tensorflow?有没有办法使用 Tensorflow 自动化迁移学习?
【发布时间】:2017-08-08 13:12:07
【问题描述】:

我正在使用 Tensorflow 构建和训练多个神经网络。这些网络正在对相关任务(自然语言处理)进行监督学习。

我所有的神经网络之间的共同点是它们共享一些早期层(有些共享另外 2 个)。

我希望能够从一个架构共享公共层的训练权重,以初始化另一个架构。

我目前做事的方式是,每次我想转移权重时,我都会编写一段单独的(临时)代码。这使我的项目变得混乱并且非常耗时。

有没有人知道一种方法可以让我自动化重量转移过程。比如说,要自动检测公共层,然后初始化相应的权重。

【问题讨论】:

    标签: machine-learning tensorflow


    【解决方案1】:

    您可以专门为感兴趣的变量集创建一个tf.Saver,只要它们具有相同的名称,您就可以在另一个图表中恢复它们。您可以使用集合来存储这些变量,然后为集合创建保护程序:

    TRANSFERABLE_VARIABLES = "transferable_variable"
    # ...
    my_var = tf.get_variable(...)
    tf.add_to_collection(TRANSFERABLE_VARIABLES, my_var)
    # ...
    saver = tf.Saver(tf.get_collection(TRANSFERABLE_VARIABLES), ...)
    

    这应该允许您在一个图表中调用save 并在另一个图表中调用restore 来传递权重。

    如果您想避免将任何内容写入磁盘,那么我认为除了手动复制/粘贴值之外别无他法。但是,这也可以通过使用集合和完全相同的构造过程在相当程度上实现自动化:

    model1_graph = create_model1()
    model2_graph = create_model2()
    
    with model1_graph.as_default(), tf.Session() as sess:
        # Train...
        # Retrieve learned weights
        transferable_weights = sess.run(tf.get_collection(TRANSFERABLE_VARIABLES))
    
    with model2_graph.as_default(), tf.Session() as sess:
        # Load weights from the other model
        for var, weight in zip(tf.get_collection(TRANSFERABLE_VARIABLES),
                               transferable_weights):
            var.load(weight, sess)
        # Continue training...
    

    同样,这仅在公共层的构造相同时才有效,因为两个图的集合中变量的顺序应该相同。

    更新:

    如果您想确保恢复的变量不用于训练,您有几种可能,尽管它们可能都需要对您的代码进行更多更改。 trainable 变量只是包含在集合tf.GrapKeys.TRAINABLE_VARIABLES 中的变量,因此当您在第二个图中创建传输的变量时,您可以只说trainable=False,并且恢复过程应该相同。如果您想更加动态并自动执行,这或多或少是可能的,但请记住这一点:必须在创建优化器之前知道必须用于训练的变量列表,并且之后不能更改(不创建新的优化器)。知道了这一点,我认为没有任何解决方案不通过传递带有来自第一个图中的可转移变量的名称的列表。例如:

    with model1_graph.as_default():
        transferable_names = [v.name for v in tf.get_collection(TRANSFERABLE_VARIABLES)]
    

    然后,在第二个图的构建过程中,在定义模型之后,就在创建优化器之前,您可以执行以下操作:

    train_vars = [v for v in tf.get_collection(tf.GrapKeys.TRAINABLE_VARIABLES)
                  if v.name not in transferable_names]
    # Assuming that `model2_graph` is the current default graph
    tf.get_default_graph().clear_collection(tf.GrapKeys.TRAINABLE_VARIABLES)
    for v in train_vars:
        tf.add_to_collection(tf.GrapKeys.TRAINABLE_VARIABLES, v)
    # Create the optimizer...
    

    另一种选择是不修改集合tf.GrapKeys.TRAINABLE_VARIABLES,而是将要优化的变量列表(示例中为train_vars)作为参数var_list 传递给优化器的minimize 方法。原则上我个人不太喜欢这样,因为我认为集合的内容应该符合它们的语义目的(毕竟代码的其他部分可能将同一个集合用于其他目的),但这取决于我猜的情况。

    【讨论】:

    • 这似乎是我正在寻找的。我将专门为共享变量创建第二个保护程序实例。然后,在恢复时,我将只加载相应的权重。
    • 我应用了你的方法。重新加载很好。我想知道是否可以动态地使恢复的变量不可训练,即仅当我重新加载这些变量时。这将允许我在恢复时不必更改我的代码。
    • @ryuzakinho 我打算写一个评论来回答这个问题,但它会太长或解释不好,所以我更新了答案。
    【解决方案2】:

    如果我理解正确,您已经保存了一些权重,希望能够在不同网络之间使用。

    在初始化特定网络时,您可以使用共享函数通过共享层构建计算图,并使用单独的函数在训练前加载权重。

    我经常有一个专门用于构建图的模块,用于构建每个部分的功能不同,即

    def build_graph():
        with tf.Graph().as_default() as graph:
            build_shared_layers()
            build_other_layers()
            build_training_ops()
            return graph
    

    build_shared_layers() 函数将设置在所有网络之间共享的变量和操作,但使用trainable=False 标志(我假设您在某处为这些层保存了权重)。您还可以为变量提供名称,以便稍后在加载函数中引用。

    然后,在训练新网络之前,只需使用 var_list 加载权重即可。您可以为此使用图形集合:

    tf.add_to_collection('var_list', some_var)
    tf.add_to_collection('var_list', another_var)
    

    然后只需获取集合并拥有一个执行以下操作的函数:

    def load_existing_weights(sess, path, var_list):
        saver = tf.train.Saver(var_list=var_list)
        ckpt = tf.train.get_checkpoint_state(path)
        saver.restore(sess, ckpt.model_checkpoint_path)
    
    ...
    ...
    with tf.Session(graph=graph) as sess:
        load_existing_weights(sess, FLAGS.save_path, var_list)
        # get on with the training below...
    

    编辑:意识到我忘了添加文档链接...如另一个答案所示,请查看 @987654321@,特别是 var_list 参数。

    【讨论】:

    • 我不明白你的意思:“假设你在某处为这些层保存了权重”。
    猜你喜欢
    • 2021-06-08
    • 2021-08-16
    • 2020-10-31
    • 2020-08-06
    • 1970-01-01
    • 2019-02-26
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多