Keras - Transfer öğrenimi - Giriş tensörü şeklini değiştirme


15

Bu yazı yapmak istediğim şeyin mümkün olmadığını gösteriyor. Ancak, buna ikna olmadım - zaten yaptığım göz önüne alındığında, yapmak istediğim şeyin neden elde edilemediğini anlamıyorum ...

Birinin şekil görüntülerinin (480, 720, 3), diğerinin ise şekil görüntülerinin (540, 960, 3) olduğu iki görüntü veri kümem var.

Aşağıdaki kodu kullanarak bir model başlattım:

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'])

Şimdi bu modeli eski veri kümesinde eğittiğime göre, giriş tensörü katmanını patlatmak ve modeli, ikinci veri kümesinin görüntü boyutlarıyla eşleşen bir şekle sahip yeni bir giriş tensörü ile doldurmak istiyorum.

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')

Bu da bu hatayı verir:

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'

Bağladığım yazıda, maz, bir modelin giriş katmanını değiştirmeyi önleyen bir boyut uyuşmazlığı olduğunu söylüyor - eğer durum buysa, önüne (480, 720, 3) bir giriş katmanını nasıl koyduğumu (224, 224, 3) görüntü bekleyen VGG16 modelinin?

Ben daha olası bir sorun benim eski modelin çıkış ben fchollet içinde söylediklerine dayanarak veriyorum olandan farklı bir şey bekliyor olduğunu düşünüyorum bu yazı . Sözdizimsel olarak kafam karıştı, ama tüm x = Layer()(x)segmentin katmanı giriş-> çıktıdan parça parça oluşturduğuna ve sadece önden farklı bir girişi attığına inanıyorum .

Gerçekten hiçbir fikrim yok ...

Birisi lütfen yapmaya çalıştığım şeyi nasıl başaracağımı aydınlatabilir mi, yoksa mümkün değilse bana neden olmadığını açıklayabilir mi?


çözdün mü
tktktk0711

Yanıtlar:


4

Yeni giriş şekilli yeni bir VGG16 model örneği oluşturarak new_shapeve tüm katman ağırlıklarının üzerine kopyalayarak bunu yapabilirsiniz. Kod kabaca

new_model = VGG16(weights=None, input_shape=new_shape, include_top=False)
for new_layer, layer in zip(new_model.layers[1:], model.layers[1:]):
    new_layer.set_weights(layer.get_weights())

Bunu inceptionV3 ile denedim ve döngü devam ettikçe yavaşlar ve yavaşlar
BachT

@ r-zip Bir hata alıyorum: 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) ve bu Giriş katmanı yani [2:]?
mLstudent33

1

VGGnet'in çıkış boyutlarının çıktı genişliği ve yüksekliği, giriş genişliğinin ve yüksekliğinin sabit bir kısmıdır, çünkü bu boyutları değiştiren tek katmanlar havuzlama katmanlarıdır. Çıktıdaki kanal sayısı son evrişimsel katmanda bulunan filtre sayısına sabitlenir. Düzleştir katmanı, şekliyle bir boyut elde etmek için bunu yeniden şekillendirir:

((input_width * x) * (input_height * x) * channels)

burada x bir ondalık sayıdır <1.

Ana nokta, Yoğun katmanlara giriş şeklinin, tüm modele girişin genişliğine ve yüksekliğine bağlı olmasıdır. Yoğun katmana şekil girişi değişmez, çünkü bu sinir ağına düğüm eklemek veya sinir ağından düğüm çıkarmak anlamına gelir.

Bundan kaçınmanın bir yolu, düzleştirilmiş bir katman (genellikle GlobalAveragePooling2D) yerine global bir havuzlama katmanı kullanmaktır (channels,). tüm model.

Bu yapıldıktan sonra ağdaki katmanların hiçbiri girişin genişliğine ve yüksekliğine bağlı değildir, böylece giriş katmanı aşağıdaki gibi bir şeyle değiştirilebilir

input_layer = InputLayer(input_shape=(480, 720, 3), name="input_1")
model.layers[0] = input_layer

model.layers[0] = input_layerTF 2.1'de benim için çalışmıyor. Hata yok, ancak katman aslında değiştirilmedi. Kopyalama ağırlıkları daha sağlam olabilir (diğer yanıtlara bakın).
z0r

0

İşte VGG modeline özgü olmayan başka bir çözüm.

Yoğun katmanın ağırlıklarının kopyalanamayacağını (ve böylece yeni başlatılacağını) unutmayın. Bu mantıklıdır, çünkü ağırlıkların şekli eski ve yeni modelde farklılık gösterir.

import keras
import numpy as np

def get_model():
    old_input_shape = (20, 20, 3)
    model = keras.models.Sequential()
    model.add(keras.layers.Conv2D(9, (3, 3), padding="same", input_shape=old_input_shape))
    model.add(keras.layers.MaxPooling2D((2, 2)))
    model.add(keras.layers.Flatten())
    model.add(keras.layers.Dense(1, activation="sigmoid"))
    model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(lr=0.0001), metrics=['acc'], )
    model.summary()
    return model

def change_model(model, new_input_shape=(None, 40, 40, 3)):
    # replace input shape of first layer
    model._layers[1].batch_input_shape = new_input_shape

    # feel free to modify additional parameters of other layers, for example...
    model._layers[2].pool_size = (8, 8)
    model._layers[2].strides = (8, 8)

    # rebuild model architecture by exporting and importing via json
    new_model = keras.models.model_from_json(model.to_json())
    new_model.summary()

    # copy weights from old model to new one
    for layer in new_model.layers:
        try:
            layer.set_weights(model.get_layer(name=layer.name).get_weights())
        except:
            print("Could not transfer weights for layer {}".format(layer.name))

    # test new model on a random input image
    X = np.random.rand(10, 40, 40, 3)
    y_pred = new_model.predict(X)
    print(y_pred)

    return new_model

if __name__ == '__main__':
    model = get_model()
    new_model = change_model(model)

0

Bu oldukça kolay olmalı kerassurgeon. Öncelikle kütüphaneyi kurmanız gerekir; Keras'ı TensorFlow (tf 2.0 ve üstü ile) veya Keras'ı ayrı bir kütüphane olarak kullanıp kullanmadığınıza bağlı olarak, farklı şekillerde kurulması gerekir.

TF'deki Keras için: pip install tfkerassurgeon( https://github.com/Raukk/tf-keras-surgeon ). Bağımsız Keras için: pip install kerassurgeon( https://github.com/BenWhetton/keras-surgeon )

Girişi değiştirmek için (örnek TF 2.0 ile; şu anda test edilmemiş kod):

from tensorflow import keras  # or import keras for standalone version
from tensorflow.keras.layers import Input

model = load_model('path/to/my/trained/model.h5')
new_input = Input(shape=(540, 960, 3), name='image_input')

# or kerassurgeon for standalone Keras
from tfkerassurgeon import delete_layer, insert_layer

model = delete_layer(model.layers[0])
# inserts before layer 0
model = insert_layer(model.layers[0], new_input)

0

@gebbissimo yanıtı, TF2'de benim için aşağıda tek bir işlevde paylaştığım küçük uyarlamalarla çalıştı:

def change_input_size(model,h,w,ch=3):
   model._layers[0]._batch_input_shape = (None,h,w,ch)
   new_model = keras.models.model_from_json(model.to_json())
   new_model.summary()
   for layer,new_layer in zip(model.layers,new_model.layers):
      new_layer.set_weights(layer.get_weights())
   return new_model

0

Keras modelinde girdi boyutunu bu şekilde değiştiriyorum. Biri giriş boyutu [Yok, Yok, 3], diğeri giriş boyutu [512,512,3] olan iki CNN modelim var. Her iki model de aynı ağırlıklara sahiptir. Set_weights (model.get_weights ()) kullanılarak, model 1 ağırlıkları model 2'ye aktarılabilir

inputs = Input((None, None, 3))
.....
model = Model(inputs=[inputs], outputs=[outputs])
model.compile(optimizer='adam', loss='mean_squared_error')
model.load_weights('my_model_name.h5')

inputs2 = Input((512, 512, 3))
....
model2 = Model(inputs=[inputs2], outputs=[outputs])
model2.compile(optimizer='adam', loss='mean_squared_error')
model2.set_weights(model.get_weights())
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.