Keras —転移学習—入力テンソル形状の変更


14

この投稿は、私が達成したいことが不可能であることを示しているようです。しかし、私はこれを確信していません-私がすでにやったことを考えると、私がやりたいことを達成できない理由がわかりません...

2つの画像データセットがあり、一方には形状(480、720、3)の画像があり、もう一方には形状(540、960、3)の画像があります。

次のコードを使用してモデルを初期化しました。

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

前のデータセットでこのモデルをトレーニングしたので、入力テンソルレイヤーをポップし、後者のデータセットの画像の寸法と一致する形状を持つ新しい入力テンソルをモデルに追加したいと思います。

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

このエラーが発生します:

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'

私がリンクした投稿では、モデルの入力レイヤーの変更を妨げる次元の不一致があるとmazが述べています。 (224、224、3)画像を期待するVGG16モデルの

私がよりありそうな問題は、以前のモデルの出力が、この投稿でfcholletが言ったことに基づいて、私がそれを与えているものとは異なる何かを期待していることだと思います。私は構文的に混乱していますが、x = Layer()(x)セグメント全体が入力から出力まで部分ごとにレイヤーを構築しており、単純に前に別の入力を投げることはそれを壊していると思います。

本当にわからないけど...

誰かが私がやろうとしていることを達成する方法を教えてもらえますか、それが不可能な場合は、なぜそうしないのか説明してください。


解決しましたか?
tktktk0711

回答:


3

これを行うには、新しい入力形状で新しいVGG16モデルインスタンスを作成し、new_shapeすべてのレイヤーウェイトをコピーします。コードは大体

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

inceptionV3でこれを試みたが、ループが継続するとして、それは遅く、遅くなる
BachT

@ r-zipエラーが発生します: 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) それが入力レイヤーなので、使用し[2:]ますか?
mLstudent33

1

VGGnetの出力寸法の出力幅と高さは、入力幅と高さの固定部分です。これは、これらの寸法を変更する層はプーリング層だけだからです。出力のチャネル数は、最後の畳み込み層のフィルター数に固定されます。平坦化レイヤーは、これにより形状を1つの次元に変更します。

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

ここで、xは10進数<1です。

主なポイントは、高密度レイヤーへの入力の形状は、モデル全体への入力の幅と高さに依存することです。高密度層への形状入力は、ニューラルネットワークでノードを追加または削除することになるため、変更できません。

これを回避する1つの方法は、フラット化レイヤー(通常はGlobalAveragePooling2D)ではなくグローバルプーリングレイヤーを使用することです。これにより、チャネルごとの平均が検出され、高密度レイヤーへの入力の形状が (channels,)の入力の形状がモデル全体。

これが完了すると、ネットワーク内のどのレイヤーも入力の幅と高さに依存しないため、入力レイヤーを次のように変更できます。

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

0

VGGモデルに固有ではない別のソリューションを次に示します。

密なレイヤーの重みはコピーできないことに注意してください(したがって、新しく初期化されます)。これは、ウェイトの形状が古いモデルと新しいモデルで異なるため、理にかなっています。

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

これはで簡単にできるはずkerassurgeonです。まず、ライブラリをインストールする必要があります。TensorFlow(tf 2.0以降)経由でKerasを使用しているか、Kerasを別のライブラリとして使用しているかに応じて、異なる方法でインストールする必要があります。

TFのKerasの場合:pip install tfkerassurgeonhttps://github.com/Raukk/tf-keras-surgeon)。スタンドアロンKerasの場合:pip install kerassurgeon https://github.com/BenWhetton/keras-surgeon

入力を置き換えるには(例:TF 2.0;現在テストされていないコード):

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の答えはTF2で私のために働いたが、1つの機能で以下で共有するわずかな適応がありました:

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モデルで入力サイズを変更する方法です。2つのCNNモデルがあり、1つは入力サイズ[なし、なし、3]で、もう1つは入力サイズ[512,512,3]です。両方のモデルの重量は同じです。set_weights(model.get_weights())を使用することにより、モデル1の重みをモデル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())
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.