如何在自定义Keras层中使用Keras层

16

我想编写自己的keras层。在这个层中,我想使用其他一些keras层。是否有办法做到像这样:

class MyDenseLayer(tf.keras.layers.Layer):
  def __init__(self, num_outputs):
    super(MyDenseLayer, self).__init__()
    self.num_outputs = num_outputs

  def build(self, input_shape):
    self.fc = tf.keras.layers.Dense(self.num_outputs)

  def call(self, input):
    return self.fc(input)

layer = MyDenseLayer(10)

当我做类似于

input = tf.keras.layers.Input(shape = (16,))
output = MyDenseLayer(10)(input)
model = tf.keras.Model(inputs = [input], outputs = [output])
model.summary()

它输出 这里输入图像描述

如何使dense层中的weights可训练?

4个回答

18

如果你查看如何添加自定义层的文档,他们建议使用.add_weight(...)方法。该方法在内部将所有权重放置在self._trainable_weights中。所以要做你想要的事情,你必须首先定义你想要使用的Keras层,构建它们,复制权重,然后构建你自己的层。如果我更新你的代码,应该是这样的:

class mylayer(tf.keras.layers.Layer):
    def __init__(self, num_outputs, num_outputs2):
        self.num_outputs = num_outputs
        super(mylayer, self).__init__()

    def build(self, input_shape):
        self.fc = tf.keras.layers.Dense(self.num_outputs)
        self.fc.build(input_shape)
        self._trainable_weights = self.fc.trainable_weights
        super(mylayer, self).build(input_shape)

    def call(self, input):
        return self.fc(input)

layer = mylayer(10)
input = tf.keras.layers.Input(shape=(16, ))
output = layer(input)
model = tf.keras.Model(inputs=[input], outputs=[output])
model.summary()

你应该能够得到你想要的。

enter image description here

谢谢Nicki Skafte的帮助。我不是很确定build的作用是什么。我查看了Github代码,看起来build的主要目的是将构建设置为True,在Dense层的情况下还会为您添加权重。然而,我不太明白为什么我们需要将构建设置为True,这有什么作用呢?谢谢~ - I-PING Ou
主要原因是在调用model.compile(...)之前,需要构建所有的层。请参考此文件 - Nicki Skafte
1
如果我在build中使用多个Keras层,应该如何处理?如何将它们的可训练权重添加到层的self._trainable_weights中?传递其列表是不适用的。 - JunjieChen
1
只需要解决它。如果有人在寻找答案:self.fc.trainable_weights的输出是一个列表,所以如果你有两个可训练层,你应该这样写: self.trainable_weights = [self.fc1.trainable_weights[0], self.fc1.trainable_weights[1], self.fc2.trainable_weights[0], self.fc2.trainable_weights[1]] - donto
1
如果它们是列表,只需将列表相加:self._trainable_weights = self.fc1.trainable_weights + self.fc2.trainable_weights 或使用列表的 extend 方法。 - LucG
显示剩余2条评论

5

将现有的层放入tf.keras.models.Model类中会更加方便和简洁。如果您定义非自定义层,例如layers、conv2d等,则这些层的参数默认情况下不可训练。

class MyDenseLayer(tf.keras.Model):
  def __init__(self, num_outputs):
    super(MyDenseLayer, self).__init__()
    self.num_outputs = num_outputs
    self.fc = tf.keras.layers.Dense(num_outputs)

  def call(self, input):
    return self.fc(input)

  def compute_output_shape(self, input_shape):
    shape = tf.TensorShape(input_shape).as_list()
    shape[-1] = self.num_outputs
    return tf.TensorShape(shape)

layer = MyDenseLayer(10)

可以参考这篇教程:https://www.tensorflow.org/guide/keras#model_subclassing


3
TF2 自定义层指南 中,他们“建议在 __init__ 方法中创建这些子层(因为子层通常会有一个 build 方法,在外部层构建时将被构建)。” 因此,只需将创建 self.fc 的代码移动到 __init__ 中即可实现您想要的效果。
class MyDenseLayer(tf.keras.layers.Layer):
  def __init__(self, num_outputs):
    super(MyDenseLayer, self).__init__()
    self.num_outputs = num_outputs
    self.fc = tf.keras.layers.Dense(self.num_outputs)

  def build(self, input_shape):
    self.built = True

  def call(self, input):
    return self.fc(input)

input = tf.keras.layers.Input(shape = (16,))
output = MyDenseLayer(10)(input)
model = tf.keras.Model(inputs = [input], outputs = [output])
model.summary()

输出:

Model: "model_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, 16)]              0         
_________________________________________________________________
my_dense_layer_2 (MyDenseLay (None, 10)                170       
=================================================================
Total params: 170
Trainable params: 170
Non-trainable params: 0

我认为在这种简单的情况下,如果build函数没有添加任何功能,你不需要为你的类定义该函数。但是,如果你添加了功能,那么建议在build函数定义的末尾调用super(MyDenseLayer, self).build(input_shape):https://keras.io/layers/writing-your-own-keras-layers/ - adamconkey
@adamconkey,如果supertf.keras.layers.Layer,那么调用super(MyDenseLayer, self).build(input_shape)等同于self.built = True。如果super是一个更复杂的层,根据你想要继承的基础层的哪个部分,你可能需要检查super().build()的实现,提取你真正想要的派生层的部分,添加新内容,并在build的末尾调用self.built = True - Maosi Chen

0

这对我来说很有效,而且简洁、清晰易读。

import tensorflow as tf


class MyDense(tf.keras.layers.Layer):
    def __init__(self, **kwargs):
        super(MyDense, self).__init__(kwargs)
        self.dense = tf.keras.layers.Dense(2, tf.keras.activations.relu)

    def call(self, inputs, training=None):
        return self.dense(inputs)


inputs = tf.keras.Input(shape=10)
outputs = MyDense(trainable=True)(inputs)
model = tf.keras.Model(inputs=inputs, outputs=outputs, name='test')
model.compile(loss=tf.keras.losses.MeanSquaredError())
model.summary()

输出:

Model: "test"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 10)]              0         
_________________________________________________________________
my_dense (MyDense)           (None, 2)                 22        
=================================================================
Total params: 22
Trainable params: 22
Non-trainable params: 0
_________________________________________________________________

请注意需要设置trainable=True。我在这里发布了一个相关问题here

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