ゼファーネットのロゴ

ConvNet の過学習について理解する

日付:

概要

ConvNet のオーバーフィッティングはディープ ラーニングとニューラル ネットワークにおける課題であり、モデルがトレーニング データから学習しすぎるため、新しいデータのパフォーマンスが低下します。この現象は、複雑な関係をモデル化できる複雑なニューラル アーキテクチャで特によく見られます。 convnet での過学習に対処することは、信頼性の高いニューラル ネットワーク モデルを構築するために重要です。この記事では、モデルの複雑さ、トレーニング データの制限、ノイズの多い特徴などの根本原因を調査し、過学習を理解して軽減するためのガイドを提供します。また、データ拡張戦略や正則化方法など、過学習を防ぐ手法についても説明します。

基本的な理解のためにこれらの記事を読むことをお勧めします。 過学習、過小学習 & バイアス分散のトレードオフ。

学習の目的

  • 過学習の原因、結果、シナリオを理解する コンブネット.
  • 学習曲線を解釈して、ニューラル ネットワーク モデルの過学習と過小学習を検出します。
  • 早期停止、ドロップアウト、バッチ正規化、正則化、データ拡張など、過学習を軽減するさまざまなテクニックを学びます。
  • これらのテクニックを以下を使用して実装します。 TensorFlow CIFAR-10 データセットで ConvNet をトレーニングするための Keras です。
  • モデルのパフォーマンスと一般化に対するさまざまな手法の影響を分析します。

目次

ConvNet でのオーバーフィッティングの一般的なシナリオ

ConvNet におけるオーバーフィッティングの一般的なシナリオをいくつか見てみましょう。

シナリオ 1: データが不十分で非常に複雑なモデル

ディープ ニューラル ネットワークなどの非常に複雑なモデルを小さなデータセットで使用すると、過剰学習が発生する可能性があります。モデルは、一般的なパターンを学習する代わりに、トレーニング例を記憶する場合があります。たとえば、画像認識のような複雑なタスクのために、わずか数百枚の画像を使用してディープ ニューラル ネットワークをトレーニングすると、過剰学習が発生する可能性があります。

結果

モデルはトレーニング データでは非常に優れたパフォーマンスを発揮する可能性がありますが、新しい未知のデータに一般化することができず、現実世界のアプリケーションではパフォーマンスが低下します。

この問題を解決するには?

より多くのトレーニング データを取得し、画像拡張を行ってデータセットを一般化します。あまり複雑でないモデルから始めて、容量が少ない場合は複雑さを増やします。 

シナリオ 2: 過剰なトレーニング

あまりにも多くのエポックにわたってモデルを継続的にトレーニングすると、過剰適合が発生する可能性があります。モデルがトレーニング データを繰り返し参照すると、基礎となるパターンを学習するのではなく、データを記憶し始める可能性があります。

結果

モデルのパフォーマンスは、トレーニング セットにますます特化されるため、目に見えないデータでは頭打ちになったり、低下したりする可能性があります。

この問題を解決するには?

早期停止を使用してモデルの過剰適合を回避し、最適なモデルを保存します。 

シナリオ 3: 正則化の無視

L1 正則化や L2 正則化などの正則化手法は、複雑なモデルにペナルティを課すことで過学習を防ぐために使用されます。正則化パラメータを無視したり不適切に調整すると、過学習が発生する可能性があります。

結果

モデルが過度に複雑になり、新しいデータにうまく一般化できなくなり、トレーニング セット以外のパフォーマンスが低下する可能性があります。

この問題を解決するには?

正則化、相互検証、ハイパーパラメータ調整を実装します。 

モデルの能力とは何ですか?

モデルの能力は、モデルが学習できるパターンのサイズと複雑さを指します。ニューラル ネットワークの場合、これは主に、ネットワークに含まれるニューロンの数とそれらがどのように接続されているかによって決まります。ネットワークがデータに十分に適合していないように見える場合は、その容量を増やしてみてください。

ネットワークの容量を増やすには、ネットワークを広くする (既存のレイヤーにユニットを増やす) か、深くする (レイヤーを追加する) ことができます。ネットワークが広いほど、より線形な関係を学習するのが容易になりますが、より深いネットワークは、より非線形な関係を好みます。どちらが優れているかはデータセットによって異なります。

学習曲線の解釈

Keras は、トレーニング時にコールバックを登録する機能を提供します。 ディープラーニングモデル。 すべての深層学習モデルをトレーニングするときに登録されるデフォルトのコールバックの 1 つは、履歴コールバックです。各エポックのトレーニング メトリクスを記録します。これには、損失と精度 (分類問題の場合)、および検証データセットが設定されている場合の損失と精度が含まれます。

履歴オブジェクトは、モデルのトレーニングに使用される fit() 関数の呼び出しから返されます。メトリクスは、返されたオブジェクトの履歴メンバーのディクショナリに保存されます。

たとえば、モデルのトレーニング後に次のコード スニペットを使用して、履歴オブジェクトに収集されたメトリクスを一覧表示できます。

# list all data in history
print(history.history.keys())

出力:

['精度', '損失', 'val_accuracy', 'val_loss']

情報タイプ

トレーニング データ内の情報は次の 2 種類であると考えることができます。

  • 信号: 信号は一般化する部分であり、モデルが新しいデータから予測を行うのに役立つ部分です。
  • ノイズ: ノイズは、トレーニング データにのみ当てはまる部分です。ノイズとは、現実世界のデータから生じるランダムな変動すべて、または実際にはモデルの予測に役立たない、付随的で有益でないパターンすべてです。ノイズは、便利そうに見えても実際はそうではない部分です。

モデルをトレーニングするとき、エポックごとにトレーニング セットの損失をプロットしてきました。これに検証データのプロットも追加します。これらのプロットを学習曲線と呼びます。深層学習モデルを効果的にトレーニングするには、深層学習モデルを解釈できる必要があります。

学習曲線

上の図では、エポックが増加するにつれてトレーニング損失が減少しますが、検証損失は最初は減少し、モデルがデータセット内に存在するノイズを捕捉し始めると増加することがわかります。ここでは、さまざまな手法を使用して ConvNet の過学習を回避する方法を見ていきます。 

過学習を回避する方法

ここまで、いくつかのシナリオと、過学習を検出するために学習曲線を解釈する方法について説明しました。ニューラル ネットワークでの過剰適合を回避するためのいくつかの方法を確認してみましょう。

方法 1: より多くのデータを使用する

データセットのサイズを増やすと、学習できるサンプルがより多様になるため、モデルの一般化が促進されます。モデルは、特定のパターン (ノイズ) がすべてのデータセットに存在するわけではないことを認識するため、データセット内に存在する重要なパターンを見つけてノイズを無視します。

方法2: 早期停止

早期停止は、トレーニング中に検証セット上のモデルのパフォーマンスを監視することにより、過剰適合を防ぐために使用される手法です。検証セットのパフォーマンスが低下し始め、モデルが過剰適合し始めていることを示すと、トレーニングが停止されます。通常、パフォーマンスを監視するために別の検証セットが使用され、指定されたエポック数にわたってパフォーマンスが改善されない場合、トレーニングは停止されます。

過小適合と過適合

方法3: ドロップアウト

過学習は、ネットワークがトレーニング データ内の偽のパターン (ノイズ) を学習することによって引き起こされることがわかっています。これらの偽のパターンを認識するために、ネットワークは多くの場合、非常に特殊な重みの組み合わせ、つまり重みの一種の「陰謀」に依存します。非常に具体的であるため、脆弱になる傾向があります。1 つを削除すると、陰謀は崩壊します。

これがドロップアウトの背後にある考え方です。これらの陰謀を打ち砕くために、トレーニングの各ステップごとに層の入力ユニットの一部をランダムに削除し、ネットワークがトレーニング データ内の偽のパターンを学習するのをはるかに困難にします。代わりに、重みパターンがより堅牢になる傾向がある、広範で一般的なパターンを検索する必要があります。 

ドロップアウトを、一種のネットワークのアンサンブルを作成するものと考えることもできます。予測はもはや 1 つの大きなネットワークによって行われるのではなく、より小規模なネットワークの委員会によって行われることになります。委員会のメンバーはさまざまな種類の間違いを犯す傾向がありますが、同時に正しく、委員会全体をどの個人よりも優れたものにします。 (決定木のアンサンブルとしてのランダム フォレストに慣れている場合は、それと同じ考えです。)

ConvNet でのオーバーフィッティング

方法 4: バッチ正規化

次に説明する特別なメソッドは、「バッチ正規化」(または「バッチノーム」) を実行します。これは、遅いまたは不安定なトレーニングを修正するのに役立ちます。

ニューラル ネットワークでは、一般に、scikit-learn の StandardScaler や MinMaxScaler などを使用して、すべてのデータを共通のスケールに配置することをお勧めします。その理由は、SGD がデータによって生成されるアクティベーションの大きさに比例してネットワークの重みをシフトするためです。非常に異なるサイズのアクティベーションを生成する傾向がある機能は、トレーニング動作を不安定にする可能性があります。

さて、ネットワークに入る前にデータを正規化するのが良いのであれば、ネットワーク内でも正規化した方が良いかもしれません。実際、これを実行できる特別な種類のレイヤー、バッチ正規化レイヤーがあります。バッチ正規化レイヤーは、入ってくる各バッチを調べ、最初に独自の平均と標準偏差でバッチを正規化し、次にトレーニング可能な 2 つの再スケーリング パラメーターを使用してデータを新しいスケールに置きます。実際、Batchnorm は入力の調整された再スケーリングのようなものを実行します。

ほとんどの場合、バッチノルムは最適化プロセスの補助として追加されます (ただし、予測パフォーマンスに役立つ場合もあります)。バッチノルムを使用したモデルは、トレーニングを完了するために必要なエポックが少なくなる傾向があります。さらに、batchnorm は、トレーニングの「行き詰まり」の原因となるさまざまな問題も修正できます。特にトレーニング中に問題が発生した場合は、モデルにバッチ正規化を追加することを検討してください。

方法 5: L1 および L2 の正則化

L1 および L2 正則化は、ニューラル ネットワーク内の大きな重みにペナルティを課すことで過学習を防ぐために使用される手法です。 L1 正則化では、重みの絶対値に比例するペナルティ項を損失関数に追加します。これにより重みの希薄化が促進され、特徴の選択につながる可能性があります。 L2 正則化は重み減衰とも呼ばれ、重みの XNUMX 乗に比例するペナルティ項を損失関数に追加します。ウェイトが大きくなりすぎるのを防ぎ、ウェイトの分布がより均等になるようにします。

L1 正則化と L2 正則化のどちらを選択するかは、多くの場合、特定の問題とモデルの望ましい特性によって異なります。

L1/L2 正則化の値が大きいと、モデルの学習が速くなくなり、学習がプラトーに達してモデルがアンダーフィットしてしまいます。 

方法6: データ拡張

機械学習モデルのパフォーマンスを向上させる最善の方法は、より多くのデータでモデルをトレーニングすることです。モデルが学習する必要がある例が多ければ多いほど、画像のどの違いが重要でどの違いが重要でないかをよりよく認識できるようになります。データが多いほど、モデルをより適切に一般化するのに役立ちます。

より多くのデータを取得する簡単な方法の 6 つは、既存のデータを使用することです。クラスを維持する方法でデータセット内の画像を変換できれば (例: MNIST 数字分類で 6 を拡張しようとすると、9 と XNUMX を区別するのが難しくなります)、分類器にそのような種類の変換を無視するように教えることができます。たとえば、写真の中で車が左を向いているか右を向いているかによって、それがトラックではなく乗用車であるという事実は変わりません。したがって、反転した画像を使用してトレーニング データを強化すると、分類器は「左または右」が無視すべき違いであることを学習します。

これがデータ拡張の背後にあるアイデア全体です。実際のデータにかなり似ている追加の偽データを追加すると、分類器が改善されます。 

過学習を回避する鍵は、モデルが適切に一般化されていることを確認することであることに注意してください。トレーニング セットだけでなく、検証セットでもモデルのパフォーマンスを常にチェックしてください。

データを使用した上記のメソッドの実装

上記のメソッドの実装手順を見てみましょう。

ステップ1: 必要なライブラリをロードする

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.callbacks import ModelCheckpoint
import keras
from keras.preprocessing import image
from keras import models, layers, regularizers
from tqdm import tqdm
import warnings
warnings.filterwarnings(action='ignore')

ステップ2: データセットのロードと前処理

#Here all the images are in the form of a numpy array
cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train / 255.0
x_test = x_test / 255.0

ステップ3: 学習データセット

x_train.shape, y_train.shape, x_test.shape, y_test.shape 

出力:

出力
np.unique(y_train)

出力:

出力
#These labels are in the order and taken from the documentaion
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck']

ステップ4: データセットからの画像の視覚化

def show_image(IMG_INDEX):
    plt.imshow(x_train[20] ,cmap=plt.cm.binary)
    plt.xlabel(class_names[y_train[IMG_INDEX][0]])
    plt.show()
show_image(20)
ConvNet でのオーバーフィッティング
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10))
model.summary()

次に、ハイパーパラメーターを初期化し、オプティマイザー、損失関数、評価メトリックを使用してモデルをコンパイルしましょう。


train_hyperparameters_config={'optim':keras.optimizers.Adam(learning_rate=0.001),
                             'epochs':20,
                              'batch_size':16
                             }
model.compile(optimizer=train_hyperparameters_config['optim'],
                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                  metrics=['accuracy'])

ステップ6: トレーニングモデル

history = model.fit(x_train, y_train, 
                        epochs=train_hyperparameters_config['epochs'], 
                        batch_size=train_hyperparameters_config['batch_size'], 
                        verbose=1,
                        validation_data=(x_test, y_test))

ステップ7: モデルを評価する

これらは履歴オブジェクトに含まれる情報を示し、それらを使用して情報曲線を作成します。

print(history.history.keys()) 
def learning_curves(history):
# Plotting Accuracy
    plt.figure(figsize=(14, 5))  # Adjust the figure size as needed
    plt.subplot(1, 2, 1)  # Subplot with 1 row, 2 columns, and index 1
    plt.plot(history.history['accuracy'], label='train_accuracy', marker='s', markersize=4)
    plt.plot(history.history['val_accuracy'], label='val_accuracy', marker='*', markersize=4)
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.legend(loc='lower right')

    # Plotting Loss
    plt.subplot(1, 2, 2)  # Subplot with 1 row, 2 columns, and index 2
    plt.plot(history.history['loss'], label='train_loss', marker='s', markersize=4)
    plt.plot(history.history['val_loss'], label='val_loss', marker='*', markersize=4)
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.legend(loc='lower right')

    plt.show()
learning_curves(history)
ConvNet でのオーバーフィッティング

曲線から、検証精度が 4 エポック後にプラトーに達し、モデルがノイズを捕捉し始めることがわかります。したがって、モデルの過学習を回避し、val_loss に基づいて最適な重みを復元するために早期停止を実装します。ニューラル ネットワークがオプティマイザーを使用して損失を削減しようとするため、val_loss を使用して早期停止を監視します。精度と検証精度はしきい値 (クラスを分離する確率 – 通常、バイナリ分類の場合は 0.5) に依存するため、データセットの不均衡がある場合は、ほとんどの場合、損失を心配する必要があります。 

ステップ8: 早期停止の実装

早期に停止することでモデルのオーバーフィットが回避されるため、モデルがオーバーフィットする心配はありません。より大きなエポック数と適切な忍耐力を選択することをお勧めします。次に、同じモデル アーキテクチャを使用し、早期停止コールバックを使用してトレーニングします。 

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.AveragePooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10))
model.summary()

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)

def model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config):
    model.compile(optimizer=train_hyperparameters_config['optim'],
                      loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                      metrics=['accuracy'])
    ht = model.fit(x_train, y_train, 
                            epochs=train_hyperparameters_config['epochs'], 
                            batch_size=train_hyperparameters_config['batch_size'],
                            callbacks=[callback],
                            verbose=1,
                            validation_data=(x_test, y_test))
    return ht

ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
ConvNet でのオーバーフィッティング

モデルが採用した最適なウェイトを知るため。 

print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

ステップ9: モデルの複雑さの増加

モデルのパフォーマンスが悪く、十分なデータをキャプチャできないため、適合が不十分です。モデルの複雑さを増やして評価する必要があります。 

model = models.Sequential()

model.add(layers.Conv2D(128, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
model.summary()
出力

合計パラメータが増加していることがわかります。これは、モデル内でより複雑な関係を見つけるのに役立ちます。注: 私たちのデータセットは 32X32 画像です。これらは比較的小さな画像です。したがって、最初により複雑なモデルを使用すると、確実にモデルが過剰適合するため、モデルの複雑性をゆっくりと高める傾向があります。

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)

learning_curves(ht)
ConvNet でのオーバーフィッティング
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

上記のグラフから、モデルが過学習していることが明確に言えるため、ドロップアウト正規化とバッチ正規化と呼ばれる別の方法を使用します。

ステップ 10: ドロップアウト レイヤーとバッチ正規化レイヤーの使用

model = models.Sequential()

model.add(layers.Conv2D(128, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(256, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(512, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.3))

model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.3))

model.add(layers.Dense(10, activation='softmax'))
model.summary()
出力
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
ConvNet でのオーバーフィッティング
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

学習グラフから、バッチ正規化層とドロップアウト層があってもモデルが過学習していることがわかります。したがって、複雑さを増すのではなく、フィルターの数を増やします。より多くの特徴を抽出するには、さらに畳み込み層を追加します。

Step11: 畳み込み層を増やす

トレーニング可能なパラメーターを減らしますが、畳み込みレイヤーを増やして、より多くの特徴を抽出します。

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))

model.add(layers.Dense(10, activation='softmax'))

model.summary()
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 5,
    'epochs': 50,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
出力
learning_curves(ht)
ConvNet でのオーバーフィッティング
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

上記の出力と学習曲線から、モデルは非常に良好に機能し、過剰適合を回避していると推測できます。トレーニングの精度と検証の精度は非常に近いです。このシナリオでは、過剰適合を軽減するためにこれ以上のメソッドは必要ありません。ただし、L1/L2 の正則化を検討します。 

ステップ 12: L1/L2 正則化の使用

from tensorflow.keras import regularizers

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l1(0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same', kernel_regularizer=regularizers.l2(0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu', kernel_regularizer=regularizers.l1_l2(0.0005, 0.0005)))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation='softmax'))

model.summary()
# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 7,
    'epochs': 70,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)
ht=model_train(model, x_train, y_train, x_test, y_test, train_hyperparameters_config)
learning_curves(ht)
ConvNet でのオーバーフィッティング
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

これで、1 という低いペナルティ スコアを使用した後でも、L2/L0.0001 正則化によってモデルが 4% アンダーフィットになったことがわかります。したがって、すべての方法を慎重に組み合わせて使用​​することをお勧めします。バッチ正規化と正則化は同様の方法でモデルに影響を与えるため、L1/L2 正則化は必要ありません。 

ステップ 13: データの拡張

tensorflow keras の ImageDataGenerator を使用します。

# creates a data generator object that transforms images
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')

# pick an image to transform
test_img = x_train[20]
img = image.img_to_array(test_img)  # convert image to numpy arry
img = img.reshape((1,) + img.shape)  # reshape image

i = 0

for batch in datagen.flow(img, save_prefix='test', save_format='jpeg'):  # this loops runs forever until we break, saving images to current directory with specified prefix
    plt.figure(i)
    plot = plt.imshow(image.img_to_array(batch[0]))
    i += 1
    if i > 4:  # show 4 images
        break

plt.show()
ConvNet でのオーバーフィッティング

これらは 4 つの拡張画像と 1 つの元の画像です。

# Create an instance of the ImageDataGenerator
datagen = ImageDataGenerator(
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)

# Create an iterator for the data generator
data_generator = datagen.flow(x_train, y_train, batch_size=32)

# Create empty lists to store the augmented images and labels
augmented_images = []
augmented_labels = []

# Loop over the data generator and append the augmented data to the lists
num_batches = len(x_train) // 32
progress_bar = tqdm(total=num_batches, desc="Augmenting data", unit="batch")

for i in range(num_batches):
    batch_images, batch_labels = next(data_generator)
    augmented_images.append(batch_images)
    augmented_labels.append(batch_labels)
    progress_bar.update(1)

progress_bar.close()

# Convert the lists to NumPy arrays
augmented_images = np.concatenate(augmented_images, axis=0)
augmented_labels = np.concatenate(augmented_labels, axis=0)

# Combine the original and augmented data
x_train_augmented = np.concatenate((x_train, augmented_images), axis=0)
y_train_augmented = np.concatenate((y_train, augmented_labels), axis=0)
出力

拡張の進行状況を知るために tqdm ライブラリを使用しました。

x_train_augmented.shape, y_train_augmented.shape
出力

これは拡張後のデータセットです。次に、このデータセットを使用してモデルをトレーニングしましょう。

model = models.Sequential()

model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.2))

model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.3))

model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPool2D((2, 2)))
model.add(layers.Dropout(0.4))

model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.5))

model.add(layers.Dense(10, activation='softmax'))

model.summary()

# Here we have used more epochs than needed since we use patience parameter which we stop the model from overfitting
train_hyperparameters_config = {
    'optim': keras.optimizers.Adam(learning_rate=0.001),
    'patience': 10,
    'epochs': 70,
    'batch_size': 32, 
}
print('Setting the callback and early stopping configurations...')
callback = tf.keras.callbacks.EarlyStopping(
    monitor='val_loss', 
    min_delta=0.001, # minimium amount of change to count as an improvement
    patience=train_hyperparameters_config['patience'], 
    restore_best_weights=True)

ht=model_train(model, x_train_augmented, y_train_augmented, x_test, y_test, train_hyperparameters_config)


learning_curves(ht)
ConvNet でのオーバーフィッティング
print('Testing ..................')
test_loss, test_acc = model.evaluate(x_test,  y_test, verbose=2)
print('test_loss : ', test_loss, 'test_accuracy : ', test_acc)

モデルがより一般化され、損失が減少していることがわかります。検証精度も向上しました。したがって、データ拡張によりモデルの精度が向上しました。 

まとめ

過学習は、深層学習、特に ConvNet のような複雑なニューラル ネットワーク アーキテクチャにおいてよくある問題です。実務者は、根本原因を理解し、それが発生するシナリオを認識することで、ConvNet での過剰適合を防ぐことができます。早期停止、ドロップアウト、バッチ正規化、正規化、データ拡張などの手法は、この問題を軽減するのに役立ちます。これらの手法を CIFAR-10 データセットに実装すると、モデルの一般化とパフォーマンスが大幅に向上することがわかりました。これらの手法を習得し、その原理を理解することで、堅牢で信頼性の高いニューラル ネットワーク モデルを作成できます。

よくある質問

Q1.過学習とは何ですか?なぜ深層学習においてそれが問題となるのでしょうか? 

A. 過学習は、モデルがノイズや無関係なパターンを含むトレーニング データを学習しすぎると発生し、その結果、新しい未確認データのパフォーマンスが低下します。これが問題となるのは、過学習モデルが効果的に一般化できず、実用性が制限されるためです。

Q2.ニューラル ネットワーク モデルの過学習を検出するにはどうすればよいですか?

A. ConvNet の過学習は、学習曲線を解釈することで検出できます。学習曲線は、エポックにわたるトレーニングと検証のメトリクス (損失、精度など) をプロットします。トレーニング メトリクスが改善し続ける一方で、検証メトリクスが改善を停止したり、低下し始めた場合、それは過剰適合の兆候です。

Q3.早期停止とは何ですか?また、それが過学習の防止にどのように役立ちますか?

A. 早期停止は、トレーニング中に検証セット上のモデルのパフォーマンスを監視し、検証セット上のパフォーマンスが低下し始め、過剰適合を示したときにトレーニング プロセスを停止する手法です。適切なタイミングでトレーニングを停止することで、モデルの過剰適合を防ぐことができます。

Q4.データ拡張は過学習の軽減にどのように役立ちますか? 

A. データ拡張とは、既存のデータに変換 (反転、回転、スケーリングなど) を適用することによって、新しい合成トレーニング データを生成するプロセスです。これにより、モデルをより多様な例に公開することでモデルの一般化が促進され、限られたトレーニング データに対する ConvNet の過学習のリスクが軽減されます。

スポット画像

最新のインテリジェンス

スポット画像