Как разделить модель Keras с непоследовательной архитектурой, такой как ResNet, на подмодели? - PullRequest
1 голос
/ 15 мая 2019

Моя модель - это resnet-152. Я хочу разрезать ее на две подмодели, и проблема во второй, я не могу понять, как построить модель из промежуточного слоя для вывода

Iпопробовал этот код из этот ответ и он не работает для меня вот мой код:

def getLayerIndexByName(model, layername):
    for idx, layer in enumerate(model.layers):
        if layer.name == layername:
            return idx

idx = getLayerIndexByName(resnet, 'res3a_branch2a')

input_shape = resnet.layers[idx].get_input_shape_at(0) # which is here in my case (None, 55, 55, 256)

layer_input = Input(shape=input_shape[1:]) # as keras will add the batch shape

# create the new nodes for each layer in the path
x = layer_input
for layer in resnet.layers[idx:]:
    x = layer(x)

# create the model
new_model = Model(layer_input, x)

И я получаю эту ошибку:

ValueError: Input 0 is incompatible with layer res3a_branch1: expected axis -1 of input shape to have value 256 but got shape (None, 28, 28, 512).

Я также попробовал эту функцию:

def split(model, start, end):
    confs = model.get_config()
    kept_layers = set()
    for i, l in enumerate(confs['layers']):
        if i == 0:
            confs['layers'][0]['config']['batch_input_shape'] = model.layers[start].input_shape
            if i != start:
                confs['layers'][0]['name'] += str(random.randint(0, 100000000)) # rename the input layer to avoid conflicts on merge
                confs['layers'][0]['config']['name'] = confs['layers'][0]['name']
        elif i < start or i > end:
            continue
        kept_layers.add(l['name'])
    # filter layers
    layers = [l for l in confs['layers'] if l['name'] in kept_layers]
    layers[1]['inbound_nodes'][0][0][0] = layers[0]['name']
    # set conf
    confs['layers'] = layers
    confs['input_layers'][0][0] = layers[0]['name']
    confs['output_layers'][0][0] = layers[-1]['name']
    # create new model
    submodel = Model.from_config(confs)
    for l in submodel.layers:
        orig_l = model.get_layer(l.name)
        if orig_l is not None:
            l.set_weights(orig_l.get_weights())
    return submodel

, и я получаю эту ошибку:

ValueError: Unknown layer: Scale

, поскольку мой resnet152 содержит слой Scale.

Вот рабочая версия:

import resnet   # pip install resnet
from keras.models import Model
from keras.layers import Input

def getLayerIndexByName(model, layername):
    for idx, layer in enumerate(model.layers):
        if layer.name == layername:
            return idx


resnet = resnet.ResNet152(weights='imagenet')

idx = getLayerIndexByName(resnet, 'res3a_branch2a')

model1 = Model(inputs=resnet.input, outputs=resnet.get_layer('res3a_branch2a').output)

input_shape = resnet.layers[idx].get_input_shape_at(0) # get the input shape of desired layer
print(input_shape[1:])
layer_input = Input(shape=input_shape[1:]) # a new input tensor to be able to feed the desired layer

# create the new nodes for each layer in the path
x = layer_input
for layer in resnet.layers[idx:]:
    x = layer(x)

# create the model
model2 = Model(layer_input, x)

model2.summary()

Вот ошибка:

ValueError: Input 0 is incompatible with layer res3a_branch1: expected axis -1 of input shape to have value 256 but got shape (None, 28, 28, 512)

1 Ответ

0 голосов
/ 21 мая 2019

Как я уже упоминал в разделе комментариев, так как модель ResNet не имеет линейной архитектуры (то есть имеет пропускаемые соединения, и слой может быть подключен к нескольким слоям), вы не можете просто пройти через слои модели одинза другим в цикле и применить слой на выходе предыдущего уровня в цикле (т.е. в отличие от моделей с линейной архитектурой, для которых этот метод работает ).

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

  1. Укажите последний слой вашей подмодели.
  2. Начните с этого слоя и найдите все подключенные к нему слои.
  3. Получите выходные данные этих связанных слоев.
  4. Примените последний слой к собранному выходному сигналу.

Очевидно, что шаг № 3 подразумевает рекурсию: получить выходные данные связанных слоев(т. е. X), нам сначала нужно найти их подключенные слои (т. е. Y), получить их выходы (т. е. выходы Y), а затем применить их к этим выходам (т. е. применить X к выходам Y).Кроме того, чтобы найти связанный слой, вам нужно немного узнать о внутренностях Кераса, которые были рассмотрены в этом ответе .Итак, мы придумали это решение:

from keras.applications.resnet50 import ResNet50
from keras import models
from keras import layers

resnet = ResNet50()

# this is the split point, i.e. the starting layer in our sub-model
starting_layer_name = 'activation_46'

# create a new input layer for our sub-model we want to construct
new_input = layers.Input(batch_shape=resnet.get_layer(starting_layer_name).get_input_shape_at(0))

layer_outputs = {}
def get_output_of_layer(layer):
    # if we have already applied this layer on its input(s) tensors,
    # just return its already computed output
    if layer.name in layer_outputs:
        return layer_outputs[layer.name]

    # if this is the starting layer, then apply it on the input tensor
    if layer.name == starting_layer_name:
        out = layer(new_input)
        layer_outputs[layer.name] = out
        return out

    # find all the connected layers which this layer
    # consumes their output
    prev_layers = []
    for node in layer._inbound_nodes:
        prev_layers.extend(node.inbound_layers)

    # get the output of connected layers
    pl_outs = []
    for pl in prev_layers:
        pl_outs.extend([get_output_of_layer(pl)])

    # apply this layer on the collected outputs
    out = layer(pl_outs[0] if len(pl_outs) == 1 else pl_outs)
    layer_outputs[layer.name] = out
    return out

# note that we start from the last layer of our desired sub-model.
# this layer could be any layer of the original model as long as it is
# reachable from the starting layer
new_output = get_output_of_layer(resnet.layers[-1])

# create the sub-model
model = models.Model(new_input, new_output)

Важные примечания:

  1. Это решение предполагает, что каждый слой в исходной модели былиспользуется только один раз, т. е. он не работает для сиамских сетей, где слой может совместно использоваться и, следовательно, может применяться более одного раза для разных входных тензоров.

  2. Если вы хотите иметь правильноеразделение модели на несколько подмоделей, тогда имеет смысл использовать только те слои для точки разделения (например, обозначенные starting_layer_name в вышеприведенном коде), которые НЕ находятся в ветви (например, в ResNet слои активации после слияния слоевэто хороший вариант, но res3a_branch2a, который вы выбрали, не очень хороший вариант, поскольку он находится в ветке).Чтобы получить более полное представление об исходной архитектуре модели, вы всегда можете построить ее диаграмму, используя служебную функцию plot_model():

    from keras.applications.resnet50 import ResNet50
    from keras.utils import plot_model
    
    resnet = ResNet50()
    plot_model(model, to_file='resnet_model.png')
    
  3. Поскольку новые узлы создаются после создания дочернего элемента-модель, не пытайтесь создать другую подмодель , которая имеет перекрытие (то есть, если она не имеет перекрытия, все в порядке!) с предыдущей подмоделью в том же прогонекод выше ;в противном случае вы можете столкнуться с ошибками.

...