Есть ли способ разделить модель keras mobilenetv2 на подмодели? - PullRequest
1 голос
/ 17 марта 2020

Я пытаюсь разделить модель mobilenetv2 на 2 части.

Сначала я хочу запустить первую часть модели, сохранить выходные данные и передать их позже во вторую модель по определенным причинам. Я пробовал код, найденный здесь , но я получаю следующую ошибку:

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

Я думаю, это потому, что модель не является последовательной. Может кто-нибудь помочь?

1 Ответ

1 голос
/ 18 марта 2020

Как я уже упоминал в моих комментариях, некоторые слои в mobile_net_v2 ожидают более одного входа, который является выходом некоторых других предыдущих слоев. Поэтому добавление их в последовательную модель по отдельности приводит к ошибкам. У меня есть альтернативное решение для вас. Используя реализацию 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)

Обратите внимание, что веса инициализируются случайным образом. Если вы хотите, чтобы веса от mobilenet_v2 тренировались на imag enet, вы можете запустить следующий код для копирования весов:

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

Он повторяется по слоям mobilenet_v2 загружается из Keras , копирует веса первой части в model_f , а остальные в model_h . Вы можете проверить, правильно ли скопированы веса, распечатав некоторые случайные веса слоев с мобильного телефона _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, вы можете отредактировать его, чтобы изменить место, где вы хотите разделить. Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...