【问题标题】:Can I share weights between keras layers but have other parameters differ?我可以在 keras 层之间共享权重但其他参数不同吗?
【发布时间】:2017-07-21 19:31:03
【问题描述】:

在 keras 中,是否可以在两层之间共享权重,但其他参数不同?考虑以下(诚然有点做作)示例:

conv1 = Conv2D(64, 3, input_shape=input_shape, padding='same')
conv2 = Conv2D(64, 3, input_shape=input_shape, padding='valid')

请注意,除了padding 之外,这些层是相同的。我可以让 keras 对两者使用相同的权重吗? (即也相应地训练网络?)

我查看了 keras 文档,section on shared layers 似乎暗示只有在图层完全相同时共享才有效。

【问题讨论】:

  • 你试过了吗?理论上,具有相同内核大小和相同输入输出通道乘积的卷积层将具有相同维度的权重张量。
  • @E_net4 确实尺寸匹配,我的问题是如何让 keras 共享权重(即我想尝试一下,我只是不知道如何:))

标签: deep-learning keras


【解决方案1】:

据我所知,这不能通过 Keras 使用的常见“API 级别”来完成。 但是,如果你再深入一点,就会发现有一些(丑陋的)方法可以共享权重。

首先,Conv2D 层的权重是在build() 函数中创建的,通过调用add_weight()

    self.kernel = self.add_weight(shape=kernel_shape,
                                  initializer=self.kernel_initializer,
                                  name='kernel',
                                  regularizer=self.kernel_regularizer,
                                  constraint=self.kernel_constraint)

对于您提供的用法(即默认 trainable/constraint/regularizer/initializer),add_weight() 没有什么特别之处,只是将权重变量附加到 _trainable_weights

    weight = K.variable(initializer(shape), dtype=dtype, name=name)
    ...
        self._trainable_weights.append(weight)

最后,由于build() 仅在尚未构建层的情况下在__call__() 内部调用,因此可以通过以下方式创建层之间的共享权重:

  1. 调用conv1.build()初始化conv1.kernelconv1.bias要共享的变量。
  2. 调用conv2.build()初始化图层。
  3. conv2.kernelconv2.bias 替换为conv1.kernelconv1.bias
  4. conv2._trainable_weights 中删除conv2.kernelconv2.bias
  5. conv1.kernelconv1.bias 附加到conv2._trainable_weights
  6. 完成模型定义。这里会调用conv2.__call__();但是,由于 conv2 已经构建,因此不会重新初始化权重。

以下代码 sn-p 可能会有所帮助:

def create_shared_weights(conv1, conv2, input_shape):
    with K.name_scope(conv1.name):
        conv1.build(input_shape)
    with K.name_scope(conv2.name):
        conv2.build(input_shape)
    conv2.kernel = conv1.kernel
    conv2.bias = conv1.bias
    conv2._trainable_weights = []
    conv2._trainable_weights.append(conv2.kernel)
    conv2._trainable_weights.append(conv2.bias)

# check if weights are successfully shared
input_img = Input(shape=(299, 299, 3))
conv1 = Conv2D(64, 3, padding='same')
conv2 = Conv2D(64, 3, padding='valid')
create_shared_weights(conv1, conv2, input_img._keras_shape)
print(conv2.weights == conv1.weights)  # True

# check if weights are equal after model fitting
left = conv1(input_img)
right = conv2(input_img)
left = GlobalAveragePooling2D()(left)
right = GlobalAveragePooling2D()(right)
merged = concatenate([left, right])
output = Dense(1)(merged)
model = Model(input_img, output)
model.compile(loss='binary_crossentropy', optimizer='adam')

X = np.random.rand(5, 299, 299, 3)
Y = np.random.randint(2, size=5)
model.fit(X, Y)
print([np.all(w1 == w2) for w1, w2 in zip(conv1.get_weights(), conv2.get_weights())])  # [True, True]

这种 hacky 权重共享的一个缺点是,在模型保存/加载后权重不会保持共享。这不会影响预测,但如果您想加载训练好的模型进行进一步微调,则可能会出现问题。

【讨论】:

  • 有趣!我不知道可以这样做。虽然无法保存共享是一个问题,但我希望能够微调模型。
  • 我没试过,但我认为你可以使用model.save_weights() 只保存权重数组而不是整个模型对象。对于微调,只需使用相同的代码重新创建模型,然后调用model.load_weights()
  • 你确定这行得通吗?我得到了 OOM,我可以从 tensorflow/core/common_runtime/bfc_allocator.cc 看出 tf 实际上是在尝试分配权重的多个副本
  • 我的问题是由于自定义层。如果这些是您共享的权重,最好在层初始化期间传递它们。这解决了我的OOM问题
  • 好吧,如果您正在编写自定义层,那么您当然不应该使用此解决方案。重写层更干净。正如我所说,这只是一个快速破解。
猜你喜欢
  • 2019-08-12
  • 2019-12-06
  • 1970-01-01
  • 2022-12-19
  • 1970-01-01
  • 1970-01-01
  • 2011-07-15
  • 2014-08-05
相关资源
最近更新 更多