【问题标题】:Accessing layer's input/output using Tensorflow 2.0 Model Sub-classing使用 Tensorflow 2.0 模型子类化访问图层输入/输出
【发布时间】:2020-04-10 11:52:03
【问题描述】:

在大学练习中,我使用了 TF2.0 的模型子类 API。这是我的代码(它是 Alexnet 架构,如果你想知道的话……):

class MyModel(Model):
    def __init__(self):
        super(MyModel, self).__init__()
        # OPS
        self.relu = Activation('relu', name='ReLU')
        self.maxpool = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), padding='valid', name='MaxPool')
        self.softmax = Activation('softmax', name='Softmax')

        # Conv layers
        self.conv1 = Conv2D(filters=96, input_shape=(224, 224, 3), kernel_size=(11, 11), strides=(4, 4), padding='same',
                            name='conv1')
        self.conv2a = Conv2D(filters=128, kernel_size=(5, 5), strides=(1, 1), padding='same', name='conv2a')
        self.conv2b = Conv2D(filters=128, kernel_size=(5, 5), strides=(1, 1), padding='same', name='conv2b')
        self.conv3 = Conv2D(filters=384, kernel_size=(3, 3), strides=(1, 1), padding='same', name='conv3')
        self.conv4a = Conv2D(filters=192, kernel_size=(3, 3), strides=(1, 1), padding='same', name='conv4a')
        self.conv4b = Conv2D(filters=192, kernel_size=(3, 3), strides=(1, 1), padding='same', name='conv4b')
        self.conv5a = Conv2D(filters=128, kernel_size=(3, 3), strides=(1, 1), padding='same', name='conv5a')
        self.conv5b = Conv2D(filters=128, kernel_size=(3, 3), strides=(1, 1), padding='same', name='conv5b')

        # Fully-connected layers

        self.flatten = Flatten()

        self.dense1 = Dense(4096, input_shape=(100,), name='FC_4096_1')
        self.dense2 = Dense(4096, name='FC_4096_2')
        self.dense3 = Dense(1000, name='FC_1000')

        # Network definition

    def call(self, x, **kwargs):
        x = self.conv1(x)
        x = self.relu(x)
        x = tf.nn.local_response_normalization(x, depth_radius=2, alpha=2e-05, beta=0.75, bias=1.0)
        x = self.maxpool(x)

        x = tf.concat((self.conv2a(x[:, :, :, :48]), self.conv2b(x[:, :, :, 48:])), 3)
        x = self.relu(x)
        x = tf.nn.local_response_normalization(x, depth_radius=2, alpha=2e-05, beta=0.75, bias=1.0)
        x = self.maxpool(x)

        x = self.conv3(x)
        x = self.relu(x)
        x = tf.concat((self.conv4a(x[:, :, :, :192]), self.conv4b(x[:, :, :, 192:])), 3)
        x = self.relu(x)
        x = tf.concat((self.conv5a(x[:, :, :, :192]), self.conv5b(x[:, :, :, 192:])), 3)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.flatten(x)

        x = self.dense1(x)
        x = self.relu(x)
        x = self.dense2(x)
        x = self.relu(x)
        x = self.dense3(x)
        return self.softmax(x)

我的目标是访问任意层的输出(为了最大化特定神经元的激活,如果您必须确切知道:))。问题是尝试访问任何层的输出时,都会出现属性错误。例如:

model = MyModel()
print(model.get_layer('conv1').output)
# => AttributeError: Layer conv1 has no inbound nodes.

我在 SO 中发现了一些与此错误有关的问题,他们都声称我必须在第一层定义输入形状,但正如您所看到的 - 它已经完成(参见 self.conv1 的定义__init__ 函数)!

我确实发现,如果我定义了一个keras.layers.Input 对象,我确实设法获得了conv1 的输出,但是尝试访问更深层会失败,例如:

model = MyModel()
I = tf.keras.Input(shape=(224, 224, 3))
model(I)
print(model.get_layer('conv1').output)
# prints Tensor("my_model/conv1/Identity:0", shape=(None, 56, 56, 96), dtype=float32)
print(model.get_layer('FC_1000').output)
# => AttributeError: Layer FC_1000 has no inbound nodes.

我用谷歌搜索了我在路上遇到的每一个异常,但没有找到答案。在这种情况下,如何访问任何层的输入/输出(或输入/输出 _shape 属性)?

【问题讨论】:

    标签: python tensorflow keras conv-neural-network tensorflow2.0


    【解决方案1】:

    在子分类模型中没有层图,它只是一段代码(模型call 函数)。创建模型类的实例时未定义层连接。因此,我们需要先调用call 方法来构建模型。

    试试这个:

    model = MyModel()
    inputs = tf.keras.Input(shape=(224,224,3))
    model.call(inputs)
    # instead of model(I) in your code.
    

    创建此模型图后。

    for i in model.layers:
      print(i.output)
    # output
    # Tensor("ReLU_7/Relu:0", shape=(?, 56, 56, 96), dtype=float32)
    # Tensor("MaxPool_3/MaxPool:0", shape=(?, 27, 27, 96), dtype=float32)
    # Tensor("Softmax_1/Softmax:0", shape=(?, 1000), dtype=float32)
    # ...
    

    【讨论】:

    • 谢谢,它正在工作!但是有 2 个问题: 1. 我仍然不明白为什么调用 model(I) 不起作用 - 当我们这样做时有什么不同(在没有明确的 call 函数的情况下调用)。 2. 一些层被重用(例如,ReLU 层) - 有没有办法访问这些层的每个激活的不同输出(我相信我可以使用简单地复制这些层的简单解决方案,但我想知道是否有更有效的解决方案)。
    • @noamgot Q1:根据我从tf.keras.models.Model github repo 中了解到的,call 方法从 Network 类调用父方法,该方法在调用子类模型对象时构建网络图,仅将输入传递给调用方法自定义(MyModel)。虽然我不确定! Q2。正如您所说,对于子分类模型,唯一的方法是复制这些层。虽然如果您找到任何解决方法,请分享!
    • 即使我使用方法call,每当我使用模型子类化时,我都会收到以下错误*** AttributeError: Layer dense_12 has no inbound nodes.。有解决办法吗?
    猜你喜欢
    • 2020-05-24
    • 1970-01-01
    • 1970-01-01
    • 2019-08-30
    • 1970-01-01
    • 2022-01-23
    • 2020-03-17
    • 2021-04-13
    • 2017-11-19
    相关资源
    最近更新 更多