Ten post wydaje się wskazywać, że to, co chcę osiągnąć, nie jest możliwe. Nie jestem jednak do tego przekonany - biorąc pod uwagę to, co już zrobiłem, nie rozumiem, dlaczego nie mogę osiągnąć tego, co chcę zrobić ...
Mam dwa zestawy danych obrazów, w których jeden ma obrazy kształtu (480, 720, 3), a drugi ma obrazy kształtu (540, 960, 3).
Zainicjowałem model przy użyciu następującego kodu:
input = Input(shape=(480, 720, 3), name='image_input')
initial_model = VGG16(weights='imagenet', include_top=False)
for layer in initial_model.layers:
layer.trainable = False
x = Flatten()(initial_model(input))
x = Dense(1000, activation='relu')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
x = Dense(1000, activation='relu')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
x = Dense(14, activation='linear')(x)
model = Model(inputs=input, outputs=x)
model.compile(loss='mse', optimizer='adam', metrics=['mae'])
Teraz, kiedy wyszkoliłem ten model na poprzednim zestawie danych, chciałbym usunąć warstwę tensora wejściowego i przygotować model z nowym tensorem wejściowym o kształcie pasującym do wymiarów obrazu tego drugiego zestawu danych.
model = load_model('path/to/my/trained/model.h5')
old_input = model.pop(0)
new_input = Input(shape=(540, 960, 3), name='image_input')
x = model(new_input)
m = Model(inputs=new_input, outputs=x)
m.save('transfer_model.h5')
co powoduje ten błąd:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2506, in save
save_model(self, filepath, overwrite, include_optimizer)
File "/home/aicg2/.local/lib/python2.7/site-packages/keras/models.py", line 106, in save_model
'config': model.get_config()
File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2322, in get_config
layer_config = layer.get_config()
File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2370, in get_config
new_node_index = node_conversion_map[node_key]
KeyError: u'image_input_ib-0'
W poście, który podłączyłem, maz stwierdza, że istnieje niedopasowanie wymiarów, które uniemożliwia zmianę warstwy wejściowej modelu - jeśli tak było, to w jaki sposób umieszczam warstwę wejściową (480, 720, 3) z przodu modelu VGG16, który oczekuje (224, 224, 3) obrazów?
Wydaje mi się, że bardziej prawdopodobnym problemem jest to, że wyniki mojego poprzedniego modelu oczekują czegoś innego niż to, co daję na podstawie tego, co fchollet mówi w tym poście . Jestem zagubiony pod względem składniowym, ale wierzę, że cały x = Layer()(x)
segment konstruuje warstwę kawałek po kawałku z input-> output i po prostu rzucanie innym wejściem z przodu go psuje.
Naprawdę nie mam pojęcia ...
Czy ktoś może mi oświecić, jak osiągnąć to, co próbuję zrobić, a jeśli nie jest to możliwe, wytłumacz mi, dlaczego nie?
Odpowiedzi:
Możesz to zrobić, tworząc nową instancję modelu VGG16 z nowym kształtem wejściowym
new_shape
i kopiując wszystkie grubości warstw. Kod jest z grubszaźródło
Traceback (most recent call last): File "predict_video11.py", line 67, in <module> new_layer.set_weights(layer.get_weights()) File "/usr/local/lib/python2.7/dist-packages/keras/engine/base_layer.py", line 1057, in set_weights 'provided weight shape ' + str(w.shape)) ValueError: Layer weight shape (3, 3, 33, 64) not compatible with provided weight shape (3, 3, 9, 64)
i to jest warstwa wejściowa, więc użyj[2:]
?Szerokość wyjściowa i wysokość wymiarów wyjściowych sieci VGGnet stanowią stałą część szerokości i wysokości wejściowej, ponieważ jedynymi warstwami, które zmieniają te wymiary, są warstwy puli. Liczba kanałów na wyjściu jest ustalona na liczbę filtrów w ostatniej warstwie splotowej. Spłaszczona warstwa przekształci to, aby uzyskać jeden wymiar kształtu:
((input_width * x) * (input_height * x) * channels)
gdzie x to część dziesiętna <1.
Głównym punktem jest to, że kształt danych wejściowych do warstw gęstych zależy od szerokości i wysokości danych wejściowych do całego modelu. Wejście kształtu do gęstej warstwy nie może się zmienić, ponieważ oznaczałoby to dodanie lub usunięcie węzłów z sieci neuronowej.
Jednym ze sposobów na uniknięcie tego jest użycie globalnej warstwy puli zamiast spłaszczania (zwykle GlobalAveragePooling2D). To pozwoli znaleźć średnią na kanał, powodując, że kształt danych wejściowych do warstw gęstych będzie taki,
(channels,)
który nie zależy od kształtu wejściowego cały model.Po wykonaniu tej czynności żadna z warstw w sieci nie jest zależna od szerokości i wysokości wejścia, więc warstwę wejściową można zmienić za pomocą czegoś takiego
źródło
model.layers[0] = input_layer
nie działa dla mnie w TF 2.1. Nie ma błędu, ale warstwa nie jest w rzeczywistości zastępowana. Wygląda na to, że kopiowanie wag może być bardziej niezawodne (zobacz inne odpowiedzi).Oto inne rozwiązanie, nie specyficzne dla modelu VGG.
Zauważ, że ciężarów gęstej warstwy nie można skopiować (i dlatego zostaną one nowo zainicjowane). Ma to sens, ponieważ kształt obciążników różni się w starym i nowym modelu.
źródło
To powinno być dość łatwe
kerassurgeon
. Najpierw musisz zainstalować bibliotekę; w zależności od tego, czy korzystasz z Keras za pośrednictwem TensorFlow (z tf 2.0 i nowszymi) lub Keras jako osobnej biblioteki, należy go zainstalować na różne sposoby.W przypadku Keras w TF:
pip install tfkerassurgeon
( https://github.com/Raukk/tf-keras-surgeon ). W przypadku samodzielnego Keras:pip install kerassurgeon
( https://github.com/BenWhetton/keras-surgeon )Aby zamienić dane wejściowe (przykład na TF 2.0; obecnie nieprzetestowany kod):
źródło
Odpowiedź @gebbissimo działała dla mnie w TF2 z niewielkimi adaptacjami, które udostępniam poniżej w jednej funkcji:
źródło
W ten sposób zmieniam rozmiar wejściowy w modelu Keras. Mam dwa modele CNN, jeden z rozmiarem wejściowym [Brak, Brak, 3], a drugi ma rozmiar wejściowy [512,512,3]. Oba modele mają te same ciężary. Za pomocą set_weights (model.get_weights ()), wagi modelu 1 można przenieść do modelu 2
źródło