Как преобразовать плотный слой в эквивалентный сверточный слой в Keras?
Я хотел бы сделать что-то похожее на документ Fully Convolutional Networks (https://people.eecs.berkeley.edu/~jonlong/long_shelhamer_fcn.pdf) с помощью Keras. У меня есть сеть, которая заканчивает выравнивание карт функций и запускает их через несколько плотных слоев. Я хотел бы загрузить весы из такой сети в одну, где плотные слои заменяются эквивалентными свертками.
В качестве примера можно использовать сеть VGG16, которая поставляется вместе с Keras, где вывод 7x7x512 последнего MaxPooling2D() сглажен, а затем переходит в слой Dense (4096). В этом случае Dense (4096) будет заменен сверткой 7x7x4096.
Моя реальная сеть немного отличается, есть слой GlobalAveragePooling2D() вместо MaxPooling2D() и Flatten(). Вывод GlobalAveragePooling2D() является двумерным тензором, и нет необходимости дополнительно его сглаживать, поэтому все плотные слои, включая первый, будут заменены на 1x1 свертки.
Я видел этот вопрос: Python keras, как преобразовать плотный слой в сверточный слой, который кажется очень похожим, если не идентичным. Проблема в том, что я не могу заставить предлагаемое решение работать, потому что (а) я использую TensorFlow в качестве бэкэнд, поэтому перестановка/фильтрация весов "неправильная", и (б) я не могу понять как загрузить вес. Загрузка старого файла весов в новую сеть с помощью model.load_weights(by_name=True)
не работает, потому что имена не совпадают (и даже если они отличаются друг от друга).
Какова должна быть перестановка при использовании TensorFlow?
Как загрузить вес? Создать одну из каждой модели, вызвать model.load_weights() для загрузки одинаковых весов, а затем скопировать некоторые дополнительные веса, которые нуждаются в перестановке?
Ответы
Ответ 1
а. Не нужно делать сложное вращение. Просто измените работу
б. Используйте get_weights() и запустите новый слой
Итерации через model.layers, создайте один и тот же слой с конфигурацией и весом нагрузки, используя set_weights или как показано ниже.
Следующий фрагмент псевдокода работает для меня. (Keras 2.0)
Псевдокод:
# find input dimensions of Flatten layer
f_dim = flatten_layer.input_shape
# Creating new Conv layer and putting dense layers weights
m_layer = model.get_layer(layer.name)
input_shape = m_layer.input_shape
output_dim = m_layer.get_weights()[1].shape[0]
W,b = layer.get_weights()
if first dense layer :
shape = (f_dim[1],f_dim[2],f_dim[3],output_dim)
new_W = W.reshape(shape)
new_layer = Convolution2D(output_dim,(f_dim[1],f_dim[2]),strides=(1,1),activation='relu',padding='valid',weights=[new_W,b])
else: (not first dense layer)
shape = (1,1,input_shape[1],output_dim)
new_W = W.reshape(shape)
new_layer = Convolution2D(output_dim,(1,1),strides=(1,1),activation='relu',padding='valid',weights=[new_W,b])
Ответ 2
Основываясь на ответе hars, я создал эту функцию для преобразования произвольного cnn в fcn:
from keras.models import Sequential
from keras.layers.convolutional import Convolution2D
from keras.engine import InputLayer
import keras
def to_fully_conv(model):
new_model = Sequential()
input_layer = InputLayer(input_shape=(None, None, 3), name="input_new")
new_model.add(input_layer)
for layer in model.layers:
if "Flatten" in str(layer):
flattened_ipt = True
f_dim = layer.input_shape
elif "Dense" in str(layer):
input_shape = layer.input_shape
output_dim = layer.get_weights()[1].shape[0]
W,b = layer.get_weights()
if flattened_ipt:
shape = (f_dim[1],f_dim[2],f_dim[3],output_dim)
new_W = W.reshape(shape)
new_layer = Convolution2D(output_dim,
(f_dim[1],f_dim[2]),
strides=(1,1),
activation=layer.activation,
padding='valid',
weights=[new_W,b])
flattened_ipt = False
else:
shape = (1,1,input_shape[1],output_dim)
new_W = W.reshape(shape)
new_layer = Convolution2D(output_dim,
(1,1),
strides=(1,1),
activation=layer.activation,
padding='valid',
weights=[new_W,b])
else:
new_layer = layer
new_model.add(new_layer)
return new_model
вы можете протестировать эту функцию следующим образом:
model = keras.applications.vgg16.VGG16()
new_model = to_fully_conv(model)