質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
87.20%
Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

解決済

ImageDataGeneratorを用いた学習で検証データの学習曲線が乖離してしまう

megadaibutsu
megadaibutsu

総合スコア1

Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1回答

0評価

0クリップ

356閲覧

投稿2021/11/11 08:01

編集2021/11/12 02:49

前提・実現したいこと

ImageDataGeneratorによってデータ拡張を行いながらセマンティックセグメンテーションに取り組んでいるのですが、検証データの学習曲線が訓練データの学習曲線と大きく乖離してしまいます。テストデータによる予測は上手くいっており、コード自体に原因があると考えているのですがその原因を特定出来ません。どなたか気付いたことを教えて頂ければと思います。
イメージ説明

発生している問題・エラーメッセージ

学習曲線が明らかにおかしくなっています。予測結果のMeanIoUは0.9程度と学習自体は上手くいっているようです。

該当のソースコード

python

import tensorflow as tf from tensorflow import keras import numpy as np import pandas as pd import matplotlib.pyplot as plt from skimage import morphology # %time from tensorflow.keras.preprocessing.image import ImageDataGenerator tf.__version__ print("Num GPUs Available: ", len(tf.config.experimental.list_physical_devices('GPU'))) data = np.load('data_all.npz') X_all = data['X'] Y_all = data['Y'] print('Shape of X: {:}'.format(X_all.shape)) print('Shape of Y: {:}'.format(Y_all.shape)) for n in range(Y_all.shape[0]): #plt.figure(figsize=(16,8)) #plt.subplot(1,2,1) #plt.imshow(Y_all[n,:,:]) Y_all[n,:,:] = morphology.dilation(Y_all[n,:,:],morphology.square(7)) #plt.subplot(1,2,2) #plt.imshow(Y_all[n,:,:]) #plt.show() X_all = X_all / 255.0 num_all = 48 np.random.seed(6) id_all = np.random.choice(num_all, num_all, replace=False) id_test = id_all[0:5] id_valid = id_all[5:10] id_train = id_all[10:48] X_test = X_all[id_test] X_train = X_all[id_train] X_valid = X_all[id_valid] Y_test = Y_all[id_test] Y_train = Y_all[id_train] Y_valid = Y_all[id_valid] print('Shape of Y_train: {:}'.format(Y_train.shape)) print('Shape of Y_valid: {:}'.format(Y_valid.shape)) print('Shape of Y_test: {:}'.format(Y_test.shape)) Y_test = Y_test[:,:,:,None] Y_train = Y_train[:,:,:,None] Y_valid = Y_valid[:,:,:,None] print('Shape of Y_train: {:}'.format(Y_train.shape)) print('Shape of Y_valid: {:}'.format(Y_valid.shape)) print('Shape of Y_test: {:}'.format(Y_test.shape)) def image_augmentation(imgs, masks, batch_size, seed): # create two instances with the same arguments # create dictionary with the input augmentation values data_gen_args = dict(featurewise_center=False, featurewise_std_normalization=False, rotation_range=10, horizontal_flip=True,) ## use this method with both images and masks image_datagen = ImageDataGenerator(**data_gen_args) mask_datagen = ImageDataGenerator(**data_gen_args) ## fit the augmentation model to the images and masks with the same seed image_datagen.fit(imgs, augment=True, seed=seed) mask_datagen.fit(masks, augment=True, seed=seed) ## set the parameters for the data to come from (images) image_generator = image_datagen.flow(imgs, shuffle=True, batch_size=batch_size, save_to_dir=None, save_prefix='out_image', seed=seed) ## set the parameters for the data to come from (masks) mask_generator = mask_datagen.flow(masks, shuffle=True, batch_size=batch_size, save_to_dir=None, save_prefix='out_mask', seed=seed) # combine generators into one which yields image and masks train_generator = zip(image_generator, mask_generator) ## return the train generator for input in the CNN return train_generator batch_size = 4 epoch_num =1000 seed = 6 train_generator = image_augmentation(X_train, Y_train,batch_size,seed) valid_generator = image_augmentation(X_valid, Y_valid,batch_size,seed) #入力層 input_ = keras.layers.Input(shape=X_train.shape[1:]) #2次元畳み込み層 zero1_1 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(input_) conv1_1 = keras.layers.Convolution2D(9, activation='relu', kernel_size=3)(zero1_1) zero1_2 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(conv1_1) conv1_2 = keras.layers.Convolution2D(9, activation='relu', kernel_size=3)(zero1_2) #2次元マックスプーリング層 maxpool1 = keras.layers.MaxPool2D(2)(conv1_2) #2次元畳み込み層 zero2_1 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(maxpool1) conv2_1 = keras.layers.Convolution2D(18, activation='relu', kernel_size=3)(zero2_1) zero2_2 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(conv2_1) conv2_2 = keras.layers.Convolution2D(18, activation='relu', kernel_size=3)(zero2_2) #入力からの2次元畳み込み層 conv_2 = keras.layers.Convolution2D(1, activation='relu', kernel_size=1)(conv2_2) #バッチ正規化 root2_1 = keras.layers.BatchNormalization()(conv_2) #2次元マックスプーリング層 maxpool2 = keras.layers.MaxPool2D(2)(conv2_2) #2次元畳み込み層 zero3_1 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(maxpool2) conv3_1 = keras.layers.Convolution2D(36, activation='relu', kernel_size=3)(zero3_1) zero3_2 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(conv3_1) conv3_2 = keras.layers.Convolution2D(36, activation='relu', kernel_size=3)(zero3_2) #入力からの2次元畳み込み層 conv_3 = keras.layers.Convolution2D(1, activation='relu', kernel_size=1)(conv3_2) #バッチ正規化 root3_1 = keras.layers.BatchNormalization()(conv_3) #2次元マックスプーリング層 maxpool3 = keras.layers.MaxPool2D(2)(conv3_2) zero4_1 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(maxpool3) conv4_1 = keras.layers.Convolution2D(72, activation='relu', kernel_size=3)(zero4_1) zero4_2 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(conv4_1) conv4_2 = keras.layers.Convolution2D(72, activation='relu', kernel_size=3)(zero4_2) #2次元アップサンプリング upsample3 = keras.layers.UpSampling2D(2)(conv4_2) #バッチ正規化 root3_2 = keras.layers.BatchNormalization()(upsample3) #別ルートのネットワーク出力の結合 concat3 = keras.layers.Concatenate()([root3_1, root3_2]) # 2次元逆畳み込み層(deconvolution layer) dezero3_1 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(concat3) deconv3_1 = keras.layers.Convolution2D(36, activation='relu', kernel_size=3)(dezero3_1) dezero3_2 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(deconv3_1) deconv3_2 = keras.layers.Convolution2D(36, activation='relu', kernel_size=3)(dezero3_2) #2次元アップサンプリング upsample2 = keras.layers.UpSampling2D(2)(deconv3_2) #バッチ正規化 root2_2 = keras.layers.BatchNormalization()(upsample2) #別ルートのネットワーク出力の結合 concat2 = keras.layers.Concatenate()([root2_1, root2_2]) # 2次元逆畳み込み層(deconvolution layer) dezero2_1 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(concat2) deconv2_1 = keras.layers.Convolution2D(18, activation='relu', kernel_size=3)(dezero2_1) dezero2_2 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(deconv2_1) deconv2_2 = keras.layers.Convolution2D(18, activation='relu', kernel_size=3)(dezero2_2) #2次元アップサンプリング upsample1 = keras.layers.UpSampling2D(2)(deconv2_2) # 2次元逆畳み込み層(deconvolution layer) dezero1_1 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(upsample1) deconv1_1 = keras.layers.Convolution2D(9, activation='relu', kernel_size=3)(dezero1_1) dezero1_2 = keras.layers.ZeroPadding2D(padding=(1, 1), data_format=None)(deconv1_1) deconv1_2 = keras.layers.Convolution2D(9, activation='relu', kernel_size=3)(dezero1_2) #バッチ正規化 root1_1 = keras.layers.BatchNormalization()(deconv1_2) #入力からの2次元畳み込み層 conv_1 = keras.layers.Convolution2D(1, activation='relu', kernel_size=1)(input_) #バッチ正規化 root1_2 = keras.layers.BatchNormalization()(conv_1) #別ルートのネットワーク出力の結合 concat1 = keras.layers.Concatenate()([root1_1, root1_2]) #クラスラベル判定のための層 output = keras.layers.Convolution2D(2, activation='softmax', kernel_size=1)(concat1) model = keras.Model(inputs=[input_], outputs=[output]) class UpdatedMeanIoU(tf.keras.metrics.MeanIoU): def __init__(self, y_true=None, y_pred=None, num_classes=None, name=None, dtype=None): super(UpdatedMeanIoU, self).__init__(num_classes = num_classes,name=name, dtype=dtype) def update_state(self, y_true, y_pred, sample_weight=None): y_pred = tf.math.argmax(y_pred, axis=-1) return super().update_state(y_true, y_pred, sample_weight) #検証誤差の小さくなるモデルを保存するコールバック関数 checkpoint_cb = keras.callbacks.ModelCheckpoint('U_net_best.h5', save_best_only=True) #学習の設定 model.compile(loss='sparse_categorical_crossentropy', optimizer=keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999), metrics=[UpdatedMeanIoU(num_classes=2)]) history = model.fit_generator(train_generator, epochs = epoch_num, steps_per_epoch = X_train.shape[0]//batch_size, validation_steps = X_valid.shape[0]//batch_size, shuffle = True, verbose = 1, validation_data = valid_generator , callbacks = [checkpoint_cb]) #モデルの保存 model.save('U_net1111_6.h5') hist_df = pd.DataFrame(history.history) hist_df.to_csv('history1111_6.csv')

試したこと

ImagedataGenerator無しでの学習、
イメージ説明
データ数の配分変更(訓練データ33枚、検証データ10枚)
ImagedataGeneratorでデータ拡張無し(バッチ数1)での学習
イメージ説明

補足情報(FW/ツールのバージョンなど)

Python3.6、Tensorflow-gpu2.6
ここにより詳細な情報を記載してください。

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

jbpb0

2021/11/11 08:12 編集

ImagedataGeneratorの有無で、使ってるデータは全く同じものですか? > ImageDataGeneratorによってデータ拡張を行いながら データ拡張をしてるのは、ImagedataGenerator有りの方だけですか?
jbpb0

2021/11/11 08:11

グラフのオレンジや青の線も、ImagedataGeneratorの有無で違うように見えるので、検証データ(赤と緑の線)だけおかしいというわけではないような
megadaibutsu

2021/11/11 08:16

はい、全く同じものになります。ImagedataGeneratorは精度向上と過学習防止の目的で利用しており、拡張前の学習曲線は過学習を起こしていると判断しています。なので、おかしいという指摘はその通りです。ただ、拡張後で学習データに対して検証データがここまで乖離しているのは拡張前後で比較しても何か問題があるように見えるため、質問させて頂きました。
jbpb0

2021/11/11 08:21

データ拡張を全くやらず、ImagedataGeneratorの有無しか違わない状態で比較したら、どうなるのでしょうか? それで、結果が大体同じになるのなら、データ拡張が精度を落としてるのではないですかね それの影響が、学習データ(オレンジと青線)よりも検証データ(赤と緑線)の方がより大きく影響受ける、的な
megadaibutsu

2021/11/11 08:26

ありがとうございます、一度ImagedataGeneratorで拡張無しの状態で学習を試してみます
megadaibutsu

2021/11/12 02:52

実際に行ってみた結果、学習曲線がそこまで乖離しない結果となりました。これを見ると拡張によって検証データがより大きく影響を受けた結果ということになるのでしょうか……。
jbpb0

2021/11/12 03:24

データ拡張は、学習データだけですか? 検証データもやってますか?
megadaibutsu

2021/11/12 03:27

学習データと検証データの両方でやっています
jbpb0

2021/11/12 03:55

詳細知らないので想像ですが、検証データの拡張で生成した画像に類似の画像が、学習データにはあまりない、のではないですかね 学習データの拡張で生成した画像と、検証データの拡張で生成した画像が、あまり似てないので、データ拡張の結果として、学習データと検証データが乖離してしまった データ拡張された学習データに「過学習」してしまったので、検証データの推定精度が下がった みたいな もし上記が起きてるのなら、検証データの拡張は今のままで、学習データの拡張で作成する画像数を大幅に増やしたら、学習データの性能と検証データの性能が近くなると思います 検証データの拡張された画像と類似の画像が、学習データの拡張で発生されやすくなるので ただし、学習データのバリエーションが大幅に増えると、学習が難しくなって、学習データと検証データの性能は近くなるけど、どちらも性能が低下する、となる可能性があります AIの性能と、データ拡張のバリエーションの兼ね合い次第ですが

まだ回答がついていません

会員登録して回答してみよう

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
87.20%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

Keras

Kerasは、TheanoやTensorFlow/CNTK対応のラッパーライブラリです。DeepLearningの数学的部分を短いコードでネットワークとして表現することが可能。DeepLearningの最新手法を迅速に試すことができます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。