【问题标题】:Is there a way to divide the keras mobilenetv2 model into submodels?有没有办法将keras mobilenetv2模型划分为子模型?
【发布时间】:2020-06-28 22:40:21
【问题描述】:

我正在尝试将mobilenetv2 模型分成两部分。

出于某些原因,我首先想运行模型的第一部分,保存输出,然后将其提供给第二个模型。我试过找到here的代码, 但我收到以下错误:

ValueError: A merge layer should be called on a list of inputs.

我认为这是因为模型不是 Sequential。 有人可以帮忙吗?

【问题讨论】:

  • 你能分享你尝试合并两层的那一行(即导致错误的那一行)吗?
  • 我认为当我添加它时,使用以下行:model_h.add(model.layers[current_layer])。感谢您的快速响应。
  • 如果您使用了您提到的链接中的代码,它应该可以工作。我假设问题出在您试图拆分mobilenetv2 的地方。并非所有 Mobilenet 层都是顺序的,也不是所有层都将单个层的输出作为输入。一些层获取多个层的输出并将它们合并。确保以这样一种方式拆分你的 mobilenetv2,使得第二部分期望单层的输出作为输入,而不是多个。没有看到你的代码,这就是我现在的所有假设。
  • 感谢您的回答。请问我可以在哪一层分割,你怎么能看到在那一层分割? Here is the link 到谷歌 colab 文件。提前谢谢巴希尔!
  • 好的,我可以将它拆分到第 block_2_add 层(第 27 层,参见 colab 笔记本)。这是连接到多个层的第一层。在这一层之后我不能再分裂了。有谁知道这个问题吗?

标签: tensorflow keras tensorflow2.0 mobilenet


【解决方案1】:

正如我在 cmets 中提到的,mobile_net_v2 中的某些层需要多个输入,这些输入是其他一些先前层的输出。因此,将它们单独添加到 sequential 模型会导致错误。我有一个替代解决方案给你。使用 this 链接中的 mobile_net_v2 实现(我自己的),我能够创建您想要的模型:

import tensorflow as tf
from tensorflow.keras import layers, Model, Sequential


def conv_block(input_tensor, c, s, t, expand=True):
    """
    Convolutional Block for mobile net v2
    Args:
        input_tensor (keras tensor): input tensor
        c (int): output channels
        s (int): stride size of first layer in the series
        t (int): expansion factor
        expand (bool): expand filters or not?

    Returns: keras tensor
    """
    first_conv_channels = input_tensor.get_shape()[-1]
    if expand:
        x = layers.Conv2D(
            first_conv_channels*t,
            1,
            1,
            padding='same',
            use_bias=False
        )(input_tensor)
        x = layers.BatchNormalization()(x)
        x = layers.ReLU(6.0)(x)
    else:
        x = input_tensor

    x = layers.DepthwiseConv2D(
        3,
        s,
        'same',
        1,
        use_bias=False
    )(x)
    x = layers.BatchNormalization()(x)
    x = layers.ReLU(6.0)(x)

    x = layers.Conv2D(
        c,
        1,
        1,
        padding='same',
        use_bias=False
    )(x)
    x = layers.BatchNormalization()(x)

    if input_tensor.get_shape() == x.get_shape() and s == 1:
        return x+input_tensor

    return x


def splitted_model(input_shape=(224,224,3)):

    input = layers.Input(shape=input_shape)

    x = layers.Conv2D(
        32,
        3,
        2,
        padding='same',
        use_bias=False
    )(input)
    x = layers.BatchNormalization()(x)
    x = layers.ReLU(6.0)(x)

    x = conv_block(x, 16, 1, 1, expand=False)
    x = conv_block(x, 24, 2, 6)
    x = conv_block(x, 24, 1, 6)

    x = conv_block(x, 32, 2, 6)
    x = conv_block(x, 32, 1, 6)
    x = conv_block(x, 32, 1, 6)

    x = conv_block(x, 64, 2, 6)
    x = conv_block(x, 64, 1, 6)
    x = conv_block(x, 64, 1, 6)
    x = conv_block(x, 64, 1, 6)

    model_f = Model(inputs=input, outputs=x)

    input_2 = layers.Input(shape=(x.shape[1:]))
    x = conv_block(input_2, 96, 1, 6)
    x = conv_block(x, 96, 1, 6)
    x = conv_block(x, 96, 1, 6)

    x = conv_block(x, 160, 2, 6)
    x = conv_block(x, 160, 1, 6)
    x = conv_block(x, 160, 1, 6)

    x = conv_block(x, 320, 1, 6)

    x = layers.Conv2D(
        1280,
        1,
        1,
        padding='same',
        use_bias=False
    )(x)
    x = layers.BatchNormalization()(x)
    x = layers.ReLU(6.0)(x)

    x = layers.GlobalAveragePooling2D()(x)


    model_h = Model(inputs=input_2, outputs=x)

    return model_f, model_h

您可以这样创建两个模型:

IMG_SIZE = 160
IMG_SHAPE = (IMG_SIZE, IMG_SIZE, 3)
model_f, model_h = splitted_model(input_shape=IMG_SHAPE)

请注意,权重是随机初始化的。如果您想在 imagenet 上训练来自 mobilenet_v2 的权重,您可以运行以下代码来复制权重:

mobile_net = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                           include_top=False,
                                           weights='imagenet')
layer_f_counter = 0
layer_h_counter = 0
for i in range(len(mobile_net.layers)):
  if layer_f_counter<len(model_f.layers):
    if len(mobile_net.layers[i].get_weights()) > 0:
      if len(model_f.layers[layer_f_counter].get_weights()) > 0:
        print(mobile_net.layers[i].name,'here', model_f.layers[layer_f_counter].name, layer_f_counter)
        model_f.layers[layer_f_counter].set_weights(mobile_net.layers[i].get_weights())
      layer_f_counter += 1
      print(layer_f_counter)
    else:
      if len(model_f.layers[layer_f_counter].get_weights()) > 0:
        continue
      else:
        layer_f_counter+=1

  else:
    if layer_h_counter<len(model_h.layers):
      if len(mobile_net.layers[i].get_weights()) > 0:
        if len(model_h.layers[layer_h_counter].get_weights()) > 0:
          print(mobile_net.layers[i].name,'here', model_h.layers[layer_h_counter].name, layer_h_counter)
          model_h.layers[layer_h_counter].set_weights(mobile_net.layers[i].get_weights())
        layer_h_counter += 1
        print(layer_h_counter)
      else:
        if len(model_h.layers[layer_h_counter].get_weights()) > 0:
          continue
        else:
          layer_h_counter+=1

它遍历从 Keras 加载的 mobilenet_v2 层,将第一部分的权重复制到 model_f,其余复制到model_h。您可以通过从 mobile_net 打印出一些随机层权重以及以下新模型来检查权重是否正确复制:

print(model_f.layers[1].get_weights()) # printing weights of first conv layer in model_f
print(mobile_net.get_layer('Conv1').get_weights()) # printing weights of fist conv layer in mobile_net

同样适用于model_h:

print(model_h.layers[-4].get_weights()) # printing weights of last conv layer in model_h
print(mobile_net.get_layer('Conv_1').get_weights()) # printing weights of last conv layer in mobile_net

请注意,我随机选择了将 moile_net 分成 model_f 和 model_h 的块,您可以对其进行编辑以更改要拆分的位置。希望对您有所帮助。

【讨论】:

  • 谢谢!我稍后会尝试!
  • 与原始模型相比,我是否有可能得到更多错误的预测?
  • 我需要编译模型吗?因为我有为每个输入获得相同预测的问题?我对第一个进行预测,并将输出用作第二个的输入。我还添加了一个密集的 softmax 层,因为我的原始模型也有它。
  • 如果你使用从训练好的 mobile_net 模型复制的层,你不需要编译,但如果你添加一个密集层,它具有随机初始化的权重,因此你必须编译和训练。跨度>
  • 是的,我有一个带有 mobilenet 基础模型的模型,我添加了一个密集层,训练了模型并保存了它。然后我导入这个模型并使用您发布的代码加载权重。我还在 splitted_model 函数中添加了密集层,但仍然得到错误的预测。我做错了什么,因为密集层的权重也被加载了吗?使用基础模型训练的 PS:tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE, include_top=False, weights='imagenet'),classes=2)
猜你喜欢
  • 2015-05-26
  • 2020-03-21
  • 1970-01-01
  • 1970-01-01
  • 2020-01-12
  • 2010-09-17
  • 1970-01-01
  • 2019-08-30
  • 1970-01-01
相关资源
最近更新 更多