【问题标题】:How do I restore subset of weights in fully connected layer?如何恢复全连接层中的权重子集?
【发布时间】:2019-03-12 12:02:58
【问题描述】:

我的目标是通过首先训练权重的子集顺序训练网络 然后训练 所有 权重。考虑给定的两种架构 here 首先从“网络 1”开始,它由一个输入标量 z_1、2 个分别具有权重 (w_11, w_21) 和偏差 (b_1, b_2) 的节点组成。 “网络 2”通过添加一个输入节点 (z_2) 扩展了“网络 1”,因此也为每个节点添加了一个标量权重 (w_12, w_22)。在“Network 2”中,(w_11, w_21) 和 (b_1, b_2) 由“Network 1”的训练结果初始化,而 (w_12, w_22) 以其他方式初始化。

我知道如何保存和恢复权重子集(参见herehere)。但是,链接中描述的方法在使用像tf.layers.dense(...) 这样的全连接层时不起作用,它仅在恢复由tf.Variable(...) 实例化的变量子集时才起作用。我可能需要为此编写一个自定义层,但我不确定。 如何实现我的目标?

为了给出一些上下文,下面的脚本保存了“Network 1”

import tensorflow as tf
import numpy as np
def generator(Z,reuse=False):
    with tf.variable_scope("restore"):
        h1 = tf.layers.dense(Z,2,activation=tf.nn.leaky_relu, name='h1')
    return h1

Z = tf.placeholder(tf.float32,[None,1])
G_sample = generator(Z)
Z_batch = np.random.uniform(-1., 1., size=[1, 1])
saver = tf.train.Saver(tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,scope="restore")
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init_op)
    saver.save(sess, 'test')
    print('restore/h1/bias:0 :', sess.run(tf.get_default_graph().get_tensor_by_name("restore/h1/bias:0")))
    print('restore/h1/kernel:0 :', sess.run(tf.get_default_graph().get_tensor_by_name("restore/h1/kernel:0")))

这给出了输出

restore/h1/bias:0 : [0. 0.]
restore/h1/kernel:0 : [[-0.7695515  1.2254907]]

下面的脚本从上面的脚本中恢复图形,并用两个权重扩展它。 注意:当 z_dim = 1 时,代码运行良好(它只是恢复与之前相同的图形),但当 z_dim = 2 时,它显然会失败,因为它不知道要在层中恢复什么权重“ h1"。

import tensorflow as tf
import numpy as np
def generator(Z,reuse=False):
    with tf.variable_scope("restore"):
            h1 = tf.layers.dense(Z,2,activation=tf.nn.leaky_relu, name='h1')
    return h1
Z = tf.placeholder(tf.float32,[None,2])
G_sample = generator(Z)
z_dim = 2
Z_batch = np.random.uniform(-1., 1., size=[1, z_dim])

reader = tf.train.NewCheckpointReader('../test/modeltest')
restore_dict = dict()
for v in tf.trainable_variables():
    tensor_name = v.name.split(':')[0]
    if reader.has_tensor(tensor_name):
        print('has tensor ', tensor_name)
        restore_dict[tensor_name] = v

print('restore_dict:', restore_dict)
init_op = tf.global_variables_initializer()
saver = tf.train.Saver(restore_dict)

with tf.Session() as sess:
    sess.run(init_op)
    saver.restore(sess, 'test')
    print('restore/h1/bias:0 :',sess.run(tf.get_default_graph().get_tensor_by_name("restore/h1/bias:0")))
    print('restore/h1/kernel:0 :',sess.run(tf.get_default_graph().get_tensor_by_name("restore/h1/kernel:0")))

非常感谢您的意见。谢谢。

【问题讨论】:

  • 欢迎来到 SO。考虑到您的目标,您是否尝试过仅将所需的变量传递给优化器?
  • 感谢您的意见。在这个阶段,我不需要考虑优化,因为我只是对将全连接层权重的子集从“网络 1”转移到“网络 2”感兴趣。

标签: python tensorflow


【解决方案1】:

TensorFlow 2 即将推出,它将 tf.keras 推广为官方高级 API。事实上,tf.layers 已被弃用,取而代之的是 tf.keras.layers。即使您仍在使用 TensorFlow 1,您也应该使用 tf.keras,因为它让一切变得如此简单,而且与流行的看法相反,它非常灵活(您可以自定义任何东西,甚至是训练循环)。

这是一个创建模型然后重用其第一层的示例。您可以直接重用图层对象(但模型实际上共享该图层,因此训练模型 2 会影响模型 1,反之亦然),或者您可以创建一个新图层并复制其权重。

import tensorflow as tf
from tensorflow import keras
import numpy as np

X_train, X_test, X_new = np.random.randn(3, 100, 2)
y_train, y_test, y_new = np.random.rand(3, 100, 1)

# Build model 1
hidden1 = keras.layers.Dense(5, activation="relu", input_shape=[2])
output1 = keras.layers.Dense(1)
model1 = keras.models.Sequential([hidden1, output1])

# Train model 1
model1.compile(loss="mse", optimizer="sgd")
history = model1.fit(X_train, y_train, epochs=10)

# Evaluate and use model 1
score = model1.evaluate(X_test, y_test)
y_pred = model1.predict(X_new)

# Build model 2, sharing the first layer with model 1
hidden2 = hidden1
output2 = keras.layers.Dense(1)
model2 = keras.models.Sequential([hidden2, output2])

# Alternatively, create a new layer and copy its weights
hidden2 = keras.layers.Dense(5, activation="relu", input_shape=[2])
output2 = keras.layers.Dense(1)
model2 = keras.models.Sequential([hidden2, output2])
hidden2.set_weights(hidden1.get_weights())

如果您真的绝对想坚持使用旧式 TensorFlow,那么您可以使用 assign() 操作将任何变量设置为任何值:

import tensorflow as tf

v1 = tf.Variable(1.0)
v2 = tf.Variable(2.0)
assign_op = v2.assign(v1)
init = tf.global_variables_initializer()

with tf.Session() as sess:
    init.run()
    print("Before:")
    print("v1 =", sess.run(v1))
    print("v2 =", sess.run(v2))
    print()
    sess.run(assign_op)
    print("After:")
    print("v1 =", sess.run(v1))
    print("v2 =", sess.run(v2))

您需要遍历所有要复制的变量并为它们创建赋值操作,也许使用tf.group() 对它们进行分组,然后运行此分组操作。但为什么? TensorFlow 现在好多了,你应该使用新样式。

希望对您有所帮助,欢迎使用 StackOverflow (SO)!

编辑

如果您想将模型 1 中的一个层的权重子集复制到模型 2 中的新层,则可以按如下方式进行。在这个例子中,我将只复制第 1 层 5 个神经元中前 3 个神经元的权重和偏差:

在上面的代码中,而不是:

hidden2.set_weights(hidden1.get_weights())

使用此代码:

weights1, biases1 = hidden1.get_weights()
weights2, biases2 = hidden2.get_weights()
weights2[:, :3] = weights1[:, :3]
biases2[:3] = biases1[:3]
hidden2.set_weights([weights2, biases2])

【讨论】:

  • 非常感谢 MiniQuark 的详细解答!但是,我仍然不明白新的 Keras 样式如何帮助我恢复全连接层中的权重子集。到目前为止,我唯一的解决方案是使用分配操作简单地将权重复制到新图中的正确位置。对此有何意见?
  • 我的荣幸。我展示的是如何将模型 1 中的一个层的所有权重复制到模型 2 的另一层,我没有将所有层的所有权重复制到新模型。当你在一个层上调用 get_weights() 时,你会得到这个层的所有变量,比如 a1、a2、a3。如果您只想将这些权重中的一些复制到新层(例如只是 a2),您还需要从新层获取权重,例如 b1、b2、b3,然后调用 set_weights([b1, a2, b3 ])。希望这会有所帮助。
猜你喜欢
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 2018-06-23
  • 2017-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多