Keras - Mentransfer pembelajaran - mengubah bentuk tensor input


15

Posting ini tampaknya menunjukkan bahwa apa yang ingin saya capai tidak mungkin. Namun, saya tidak yakin dengan ini - mengingat apa yang telah saya lakukan, saya tidak melihat mengapa apa yang ingin saya lakukan tidak dapat dicapai ...

Saya memiliki dua dataset gambar di mana satu memiliki gambar bentuk (480, 720, 3) sementara yang lain memiliki gambar bentuk (540, 960, 3).

Saya menginisialisasi model menggunakan kode berikut:

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

Sekarang saya telah melatih model ini pada dataset sebelumnya, saya ingin mematikan lapisan tensor input dan menambahkan model dengan tensor input baru dengan bentuk yang sesuai dengan dimensi gambar dari dataset yang terakhir.

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

yang menghasilkan kesalahan ini:

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'

Dalam posting yang saya tautkan, maz menyatakan bahwa ada ketidakcocokan dimensi yang mencegah perubahan lapisan input model - jika ini yang terjadi, lalu bagaimana saya meletakkan (480, 720, 3) lapisan input di depan dari model VGG16 yang mengharapkan (224, 224, 3) gambar?

Saya pikir masalah yang lebih mungkin adalah bahwa keluaran model lama saya mengharapkan sesuatu yang berbeda dari apa yang saya berikan berdasarkan apa yang dikatakan fchollet dalam posting ini . Saya bingung secara sintaksis, tetapi saya percaya seluruh x = Layer()(x)segmen sedang membangun sepotong demi sepotong dari input-> output dan hanya melemparkan input yang berbeda di depan adalah merusaknya.

Aku benar-benar tidak tahu ...

Dapatkah seseorang tolong beri tahu saya bagaimana mencapai apa yang saya coba lakukan atau, jika tidak mungkin, jelaskan kepada saya mengapa tidak?


Sudahkah Anda menyelesaikannya?
tktktk0711

Jawaban:


4

Anda dapat melakukan ini dengan membuat instance model VGG16 baru dengan bentuk input baru new_shapedan menyalin semua bobot layer. Kode ini kira-kira

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

Sudah mencoba ini dengan inceptionV3, dan semakin lambat saat loop berlanjut
BachT

@ r-zip Saya mendapatkan kesalahan: 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) dan itu adalah lapisan Input jadi gunakan [2:]?
mLstudent33

1

Lebar dan tinggi keluaran dari dimensi keluaran VGGnet adalah bagian tetap dari lebar dan tinggi masukan karena satu-satunya lapisan yang mengubah dimensi tersebut adalah lapisan penyatuan. Jumlah saluran dalam output ditetapkan ke jumlah filter di lapisan convolutional terakhir. Lapisan rata akan membentuk kembali ini untuk mendapatkan satu dimensi dengan bentuk:

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

di mana x adalah beberapa desimal <1.

Poin utama adalah bahwa bentuk input ke lapisan padat tergantung pada lebar dan tinggi input ke seluruh model. Input bentuk ke lapisan padat tidak dapat berubah karena ini berarti menambah atau menghapus node dari jaringan saraf.

Salah satu cara untuk menghindari hal ini adalah dengan menggunakan lapisan pengumpulan global daripada lapisan rata (biasanya GlobalAveragePooling2D) ini akan menemukan rata-rata per saluran menyebabkan bentuk input ke lapisan Padat hanya menjadi (channels,)yang tidak tergantung pada bentuk input untuk seluruh model.

Setelah ini dilakukan, tidak ada lapisan dalam jaringan yang bergantung pada lebar dan tinggi input sehingga lapisan input dapat diubah dengan sesuatu seperti

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

model.layers[0] = input_layertidak bekerja untuk saya di TF 2.1. Tidak ada kesalahan, tetapi layer sebenarnya tidak diganti. Sepertinya menyalin bobot mungkin lebih kuat (lihat jawaban lain).
z0r

0

Berikut adalah solusi lain, tidak spesifik untuk model VGG.

Perhatikan, bahwa bobot lapisan padat tidak dapat disalin (dan karenanya akan diinisialisasi baru). Ini masuk akal, karena bentuk bobotnya berbeda dengan model lama dan baru.

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

Ini seharusnya cukup mudah kerassurgeon. Pertama, Anda perlu menginstal perpustakaan; tergantung pada apakah Anda menggunakan Keras melalui TensorFlow (dengan tf 2.0 ke atas) atau Keras sebagai pustaka yang terpisah, perlu diinstal dengan cara yang berbeda.

Untuk Keras di TF: pip install tfkerassurgeon( https://github.com/Raukk/tf-keras-surgeon ). Untuk mandiri mandiri: pip install kerassurgeon( https://github.com/BenWhetton/keras-surgeon )

Untuk mengganti input (misalnya dengan TF 2.0; kode yang saat ini belum diuji):

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 menjawab bekerja untuk saya di TF2 dengan hanya adaptasi kecil yang saya bagikan di bawah ini dalam satu fungsi:

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

Ini cara saya mengubah ukuran input dalam model Keras. Saya memiliki dua model CNN, satu dengan ukuran input [Tidak ada, Tidak ada, 3] sementara yang lain memiliki ukuran input [512,512,3]. Kedua model memiliki bobot yang sama. Dengan menggunakan set_weights (model.get_weights ()), bobot model 1 dapat ditransfer ke model 2

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())
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.