web-dev-qa-db-ja.com

resnet50転移学習中の大規模なオーバーフィット

これはCNNを使って何かをする最初の試みなので、私はおそらく非常に愚かなことをしています-しかし、どこが間違っているのか理解できません...

モデルは順調に学習しているようですが、検証の精度は(最初のエポックの後でも)改善されておらず、検証の損失は実際に時間とともに増加しています。 (1エポック後)私は過剰適合しているようには見えません-他の方法でオフにする必要があります。

通常のネットワーク動作

私はCNNネットワークをトレーニングしています-さまざまな植物(1000クラス)の約10万枚の画像があり、ResNet50を微調整してマルチクラス分類器を作成したいと考えています。画像のサイズはさまざまですが、次のようにロードします。

from keras.preprocessing import image                  

def path_to_tensor(img_path):
    # loads RGB image as PIL.Image.Image type
    img = image.load_img(img_path, target_size=(IMG_HEIGHT, IMG_HEIGHT))
    # convert PIL.Image.Image type to 3D tensor with shape (IMG_HEIGHT, IMG_HEIGHT, 3)
    x = image.img_to_array(img)
    # convert 3D tensor to 4D tensor with shape (1, IMG_HEIGHT, IMG_HEIGHT, 3) and return 4D tensor
    return np.expand_dims(x, axis=0)

def paths_to_tensor(img_paths):
    list_of_tensors = [path_to_tensor(img_path) for img_path in img_paths] #can use tqdm(img_paths) for data
    return np.vstack(list_of_tensors)enter code here

データベースは大きく(メモリに収まらない)、ディスクからの読み取りと拡張の両方を提供するために独自のジェネレータを作成する必要がありました。 (私はKerasが.flow_from_directory()を持っていることを知っています-しかし私のデータはこのように構造化されていません-それは100kのメタデータファイルと混合された100kの画像のダンプです)。私はおそらくそれらをよりよく構造化するスクリプトを作成し、独自のジェネレーターを作成するべきではありませんでしたが、問題はおそらく他の場所にあります。

以下のジェネレーターのバージョンでは、当面の間、拡張は行われません-再スケーリングのみです。

def generate_batches_from_train_folder(images_to_read, labels, batchsize = BATCH_SIZE):    

    #Generator that returns batches of images ('xs') and labels ('ys') from the train folder
    #:param string filepath: Full filepath of files to read - this needs to be a list of image files
    #:param np.array: list of all labels for the images_to_read - those need to be one-hot-encoded
    #:param int batchsize: Size of the batches that should be generated.
    #:return: (ndarray, ndarray) (xs, ys): Yields a Tuple which contains a full batch of images and labels. 

    dimensions = (BATCH_SIZE, IMG_HEIGHT, IMG_HEIGHT, 3)

    train_datagen = ImageDataGenerator(
        rescale=1./255,
        #rotation_range=20,
        #zoom_range=0.2, 
        #fill_mode='nearest',
        #horizontal_flip=True
    )

    # needs to be on a infinite loop for the generator to work
    while 1:
        filesize = len(images_to_read)

        # count how many entries we have read
        n_entries = 0
        # as long as we haven't read all entries from the file: keep reading
        while n_entries < (filesize - batchsize):

            # start the next batch at index 0
            # create numpy arrays of input data (features) 
            # - this is already shaped as a tensor (output of the support function paths_to_tensor)
            xs = paths_to_tensor(images_to_read[n_entries : n_entries + batchsize])

            # and label info. Contains 1000 labels in my case for each possible plant species
            ys = labels[n_entries : n_entries + batchsize]

            # we have read one more batch from this file
            n_entries += batchsize

            #perform online augmentation on the xs and ys
            augmented_generator = train_datagen.flow(xs, ys, batch_size = batchsize)

        yield  next(augmented_generator)

これは私が私のモデルを定義する方法です:

def get_model():

    # define the model
    base_net = ResNet50(input_shape=DIMENSIONS, weights='imagenet', include_top=False)

    # Freeze the layers which you don't want to train. Here I am freezing all of them
    for layer in base_net.layers:
        layer.trainable = False

    x = base_net.output

    #for resnet50
    x = Flatten()(x)
    x = Dense(512, activation="relu")(x)
    x = Dropout(0.5)(x)
    x = Dense(1000, activation='softmax', name='predictions')(x)

    model = Model(inputs=base_net.input, outputs=x)

    # compile the model 
    model.compile(
        loss='categorical_crossentropy',
        optimizer=optimizers.Adam(1e-3),
        metrics=['acc'])

    return model

したがって、結果として、およそ70kの画像に対して1,562,088のトレーニング可能なパラメーターがあります。

次に、5分割交差検証を使用しますが、モデルはどのフォールドでも機能しないため、ここに完全なコードを含めません。関連するビットは次のとおりです。

trial_fold = temp_model.fit_generator(
                train_generator,
                steps_per_Epoch = len(X_train_path) // BATCH_SIZE,
                epochs = 50,
                verbose = 1,
                validation_data = (xs_v,ys_v),#valid_generator,
                #validation_steps= len(X_valid_path) // BATCH_SIZE,
                callbacks = callbacks,
                shuffle=True)

私はさまざまなことを行いました-私のジェネレーターが実際に機能していることを確認し、完全に接続されたレイヤーのサイズを小さくして、ネットワークの最後のいくつかのレイヤーで遊んでみました。

ネットワーク内のパラメーターの数が多すぎるとは思いません。他の人もほとんど同じことを行って精度が0.5に近づいたことを知っていますが、私のモデルは狂ったように過剰適合しているようです。これに取り組む方法についてのアイデアは大歓迎です!

更新1:

.flow_from_directory()プロシージャで動作するように、再発明をやめてファイルでソートするのをやめることにしました。正しいフォーマット(以下のIoannis Nasiosコメントによってトリガーされます)をインポートしていることを確認するために-kerasのresnet50アプリケーションからpreprocessing_unit()に確実に移動しました。

また、モデルが実際に有用なものを生成しているかどうかを確認することにしました。データセットのボットレネックフィーチャを計算し、ランダムフォレストを使用してクラスを予測しました。それはうまくいき、約0.4の精度を得ました

だから、私は間違いなく私の画像の入力フォーマットに問題があったと思います。次のステップとして、モデルを微調整し(新しいトップレイヤーを使用)、問題が残っているかどうかを確認します...

更新2:

問題は画像の前処理にあったと思います。最終的には微調整を行わず、ボットネックレイヤーを抽出してlinear_SVC()をトレーニングしたところ、トレーニングの約60%、テストデータセットの約45%の精度が得られました。

8
morienor

問題の回避策はありますか?そうでない場合、これは、Resnetのバッチノルムレイヤーの問題である可能性があります。 kerasバッチノルムレイヤーがトレーニングとテスト中に非常に異なる動作をする場合と同様の問題にも直面しました。したがって、次の方法ですべてのbnレイヤーをフリーズできます。

BatchNorm()(training=False)

その後、同じデータセットでネットワークを再トレーニングしてください。トレーニング中にトレーニングフラグを次のように設定する必要があることに注意してください。

import keras.backend as K K.set_learning_phase(1)

テスト中は、このフラグを0に設定します。上記の変更を行った後は機能するはずです。

他の問題の解決策を見つけた場合は、ここに投稿してください。そうすれば、他の人がそれを利用できるようになります。

ありがとうございました。

3
Ankit Dixit

ImageDataGeneratorでpreprocessing_function引数を使用する必要があります。

 train_datagen = ImageDataGenerator(preprocessing_function=keras.applications.resnet50.preprocess_input)

これにより、使用している事前トレーニング済みネットワークで期待どおりに画像が事前処理されます。

3
Sesquipedalism

また、非常に小さなデータセットで作業していて、検証の正確さがどこかの時点で止まっているという同じ問題に遭遇しましたが、トレーニングの正確さは向上し続けています。また、時間の経過とともに検証の損失が大きくなっていることにも気づきました。参考までに、Resnet 50およびInceptionV3モデルを使用しています。

インターネットで少し調べたところ、この問題をKerasのBatch Normalizationレイヤーの実装に関連付けるgithubに関する議論が行われているのを発見しました。上記の問題は、転移学習を適用してネットワークを微調整するときに発生します。同じ問題があるかどうかはわかりませんが、Githubに以下のリンクを追加しました。この問題の詳細を読み、同じ問題の影響を受けているかどうかを理解するのに役立ついくつかのテストを適用してみてください。

プルリクエストとディスカッションへのGithubリンク

1
Shazam Kasher

ここでスタンフォード大学による微調整と転移学習に関するいくつかの説明

  1. 非常に異なるデータセットとimage-netデータセットからの非常に少ないデータセット-異なるステージから線形分類器を試してください

要約すると

データセットは非常に小さいため、以前のレイヤーから特徴を抽出し、その上に分類子をトレーニングして、問題がまだ存在するかどうかを確認することができます。

0
thefifthjack005

問題は、各クラスのデータセットが小さすぎることです。 100kサンプル/ 1000クラス= 1クラスあたり〜100サンプル。それには少なすぎます。ネットワークはすべての例を重み行列で記憶することができますが、一般化のためには、もっと多くの例が必要です。最も一般的なクラスのみを使用して、何が起こったかを理解してください。

0
grzegorz700