제퍼넷 로고

ConvNet의 과적합 이해

시간

개요

ConvNets의 과적합은 모델이 훈련 데이터에서 너무 많은 것을 학습하여 새로운 데이터에 대한 성능이 저하되는 딥 러닝 및 신경망에서 문제가 됩니다. 이 현상은 특히 복잡한 관계를 모델링할 수 있는 복잡한 신경 아키텍처에서 널리 나타납니다. 신뢰할 수 있는 신경망 모델을 구축하려면 컨브넷의 과적합을 해결하는 것이 중요합니다. 이 문서에서는 과적합을 이해하고 완화하며 모델 복잡성, 제한된 훈련 데이터, 잡음이 있는 기능과 같은 근본 원인을 조사하는 방법에 대한 가이드를 제공합니다. 또한 데이터 증대 전략 및 정규화 방법과 같은 과적합을 방지하는 기술에 대해서도 설명합니다.

기본적인 이해를 위해 이 기사를 읽는 것이 좋습니다. 과적합, 과소적합편향 분산 트레이드오프.

목표 학습

  • 과적합의 원인, 결과 및 시나리오를 이해합니다. ConvNet.
  • 학습 곡선을 해석하여 신경망 모델의 과적합과 과소적합을 감지합니다.
  • 조기 중지, 드롭아웃, 배치 정규화, 정규화, 데이터 증대 등 과적합을 완화하는 다양한 기술을 알아보세요.
  • 다음을 사용하여 이러한 기술을 구현합니다. TensorFlow CIFAR-10 데이터 세트에서 ConvNet을 훈련하는 Keras입니다.
  • 모델 성능 및 일반화에 대한 다양한 기술의 영향을 분석합니다.

차례

ConvNet의 과적합에 대한 일반적인 시나리오

ConvNet에서 과적합에 대한 몇 가지 일반적인 시나리오를 살펴보겠습니다.

시나리오 1: 데이터가 부족한 매우 복잡한 모델

소규모 데이터 세트에 심층 신경망과 같은 매우 복잡한 모델을 사용하면 과적합이 발생할 수 있습니다. 모델은 일반적인 패턴을 학습하는 대신 훈련 예시를 기억할 수 있습니다. 예를 들어, 이미지 인식과 같은 복잡한 작업을 위해 단 몇 백 개의 이미지만으로 심층 신경망을 훈련하면 과적합이 발생할 수 있습니다.

결과

모델은 훈련 데이터에서는 매우 잘 작동할 수 있지만, 보이지 않는 새로운 데이터로 일반화하는 데 실패하여 실제 애플리케이션에서는 성능이 저하될 수 있습니다.

이 문제를 해결하는 방법은 무엇입니까?

더 많은 교육 데이터를 얻고 이미지 확대를 수행하여 데이터 세트를 일반화합니다. 덜 복잡한 모델로 시작하고 용량이 적으면 복잡성을 늘리십시오. 

시나리오 2: 과도한 훈련

너무 많은 epoch 동안 모델을 지속적으로 훈련하면 과적합이 발생할 수 있습니다. 모델이 훈련 데이터를 반복적으로 보면 기본 패턴을 배우기보다는 이를 기억하기 시작할 수도 있습니다.

결과

모델의 성능은 훈련 세트에 점점 더 전문화됨에 따라 보이지 않는 데이터로 인해 정체되거나 심지어 저하될 수도 있습니다.

이 문제를 해결하는 방법은 무엇입니까?

모델이 과적합되는 것을 방지하고 최상의 모델을 저장하려면 조기 중지를 사용하세요. 

시나리오 3: 정규화 무시

L1 또는 L2 정규화와 같은 정규화 기술은 복잡한 모델에 페널티를 적용하여 과적합을 방지하는 데 사용됩니다. 정규화 매개변수를 무시하거나 부적절하게 조정하면 과적합이 발생할 수 있습니다.

결과

모델이 지나치게 복잡해지고 새 데이터에 대해 잘 일반화되지 않아 훈련 세트 외부에서 성능이 저하될 수 있습니다.

이 문제를 해결하는 방법은 무엇입니까?

정규화, 교차 검증, 하이퍼 매개변수 튜닝을 구현합니다. 

모델의 용량은 얼마입니까?

모델의 용량은 학습할 수 있는 패턴의 크기와 복잡성을 나타냅니다. 신경망의 경우 이는 뉴런의 수와 뉴런이 어떻게 연결되어 있는지에 따라 크게 결정됩니다. 네트워크가 데이터에 적합하지 않은 것으로 나타나면 용량을 늘려야 합니다.

네트워크를 더 넓게 만들거나(기존 레이어에 더 많은 단위 추가) 네트워크를 더 깊게 만들어(더 많은 레이어 추가) 네트워크의 용량을 늘릴 수 있습니다. 네트워크가 넓을수록 선형 관계를 더 쉽게 학습할 수 있는 반면, 네트워크가 깊을수록 비선형 관계를 선호합니다. 어느 것이 더 나은지는 데이터 세트에 따라 다릅니다.

학습 곡선의 해석

Keras는 교육할 때 콜백을 등록하는 기능을 제공합니다. 딥러닝 모델. 모든 딥러닝 모델 학습 시 등록되는 기본 콜백 중 하나가 History 콜백입니다. 각 에포크에 대한 훈련 지표를 기록합니다. 여기에는 손실 및 정확도(분류 문제의 경우)와 검증 데이터 세트(설정된 경우)의 손실 및 정확도가 포함됩니다.

기록 객체는 모델을 훈련하는 데 사용되는 fit() 함수 호출에서 반환됩니다. 측정항목은 반환된 개체의 기록 구성원에 있는 사전에 저장됩니다.

예를 들어, 모델이 훈련된 후 다음 코드 조각을 사용하여 기록 개체에서 수집된 측정항목을 나열할 수 있습니다.

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

출력:

['정확도', '손실', 'val_accuracy', 'val_loss']

정보 유형

훈련 데이터의 정보는 두 가지 종류로 간주될 수 있습니다.

  • 신호: 신호는 일반화하는 부분, 즉 모델이 새로운 데이터로부터 예측을 하는 데 도움을 줄 수 있는 부분입니다.
  • 소음: 노이즈는 훈련 데이터에만 해당되는 부분입니다. 노이즈는 실제 데이터에서 발생하는 모든 무작위 변동이거나 모델이 예측하는 데 실제로 도움이 되지 않는 모든 부수적이고 정보가 없는 패턴입니다. 소음은 부품이 유용해 보일 수도 있지만 실제로는 그렇지 않다는 것입니다.

모델을 훈련할 때 우리는 훈련 세트 에포크의 손실을 에포크별로 플로팅했습니다. 여기에 검증 데이터의 플롯도 추가하겠습니다. 이러한 플롯을 우리는 학습 곡선이라고 부릅니다. 딥러닝 모델을 효과적으로 훈련하려면 모델을 해석할 수 있어야 합니다.

학습 곡선

위 그림에서 우리는 에포크가 증가함에 따라 훈련 손실이 감소하는 것을 볼 수 있지만, 검증 손실은 처음에는 감소하고 모델이 데이터 세트에 존재하는 노이즈를 포착하기 시작하면 증가합니다. 이제 다양한 기법을 통해 ConvNet에서 과적합을 방지하는 방법을 살펴보겠습니다. 

과적합을 방지하는 방법

이제 몇 가지 시나리오와 과적합을 감지하기 위해 학습 곡선을 해석하는 방법을 살펴보았습니다. 신경망의 과적합을 방지하는 몇 가지 방법을 확인해 보겠습니다.

방법1: 더 많은 데이터 사용

데이터 세트의 크기를 늘리면 학습할 수 있는 예시가 더 다양해지기 때문에 모델이 더 잘 일반화되는 데 도움이 될 수 있습니다. 모델은 데이터 세트에 존재하는 중요한 패턴을 찾고 특정 패턴(노이즈)이 모든 데이터 세트에 존재하지 않는다는 것을 모델이 인식하므로 노이즈를 무시합니다.

방법2: 조기 중단

조기 중지는 훈련 중에 검증 세트에 대한 모델 성능을 모니터링하여 과적합을 방지하는 데 사용되는 기술입니다. 검증 세트의 성능이 저하되기 시작하면 훈련이 중지됩니다. 이는 모델이 과적합되기 시작했음을 나타냅니다. 일반적으로 성능을 모니터링하기 위해 별도의 검증 세트가 사용되며, 지정된 에포크 수 동안 성능이 향상되지 않으면 훈련이 중지됩니다.

과소적합과 과적합

방법3: 드롭아웃

우리는 훈련 데이터에서 네트워크가 가짜 패턴(노이즈)을 학습함으로써 과적합이 발생한다는 것을 알고 있습니다. 이러한 가짜 패턴을 인식하기 위해 네트워크는 일종의 가중치 "음모"인 매우 구체적인 가중치 조합에 의존하는 경우가 많습니다. 너무 구체적이기 때문에 취약한 경향이 있습니다. 하나를 제거하면 음모가 무너집니다.

이것이 드롭아웃의 기본 아이디어입니다. 이러한 음모를 해체하기 위해 우리는 훈련의 모든 단계에서 레이어의 입력 단위 중 일부를 무작위로 삭제하여 네트워크가 훈련 데이터에서 이러한 가짜 패턴을 학습하는 것을 훨씬 더 어렵게 만듭니다. 대신, 가중치 패턴이 더 견고한 경향이 있는 광범위하고 일반적인 패턴을 검색해야 합니다. 

드롭아웃을 일종의 네트워크 앙상블 생성으로 생각할 수도 있습니다. 예측은 더 이상 하나의 대규모 네트워크가 아니라 소규모 네트워크 위원회에 의해 이루어집니다. 위원회의 개인은 다양한 종류의 실수를 저지르는 경향이 있지만 동시에 옳은 행동을 함으로써 위원회 전체를 어떤 개인보다 더 좋게 만듭니다. (결정 트리의 앙상블인 랜덤 포레스트에 익숙하다면 같은 생각입니다.)

ConvNet의 과적합

방법4: 일괄 정규화

우리가 살펴볼 다음 특별한 방법은 느리거나 불안정한 훈련을 수정하는 데 도움이 될 수 있는 "배치 정규화"(또는 "배치 표준")를 수행합니다.

신경망을 사용하면 일반적으로 scikit-learn의 StandardScaler 또는 MinMaxScaler와 같은 것을 사용하여 모든 데이터를 공통 규모에 배치하는 것이 좋습니다. 그 이유는 SGD가 데이터가 생성하는 활성화 규모에 비례하여 네트워크 가중치를 이동하기 때문입니다. 매우 다양한 크기의 활성화를 생성하는 경향이 있는 기능은 훈련 동작을 불안정하게 만들 수 있습니다.

이제 데이터가 네트워크에 들어가기 전에 정규화하는 것이 좋다면, 아마도 네트워크 내부에서도 정규화하는 것이 더 나을 것입니다! 실제로 이를 수행할 수 있는 특별한 종류의 계층인 배치 정규화 계층이 있습니다. 배치 정규화 계층은 각 배치가 들어오는 대로 살펴보고 먼저 자체 평균 및 표준 편차로 배치를 정규화한 다음 훈련 가능한 두 개의 크기 조정 매개변수를 사용하여 데이터를 새로운 척도에 배치합니다. 실제로 Batchnorm은 입력에 대해 일종의 조정된 재조정을 수행합니다.

대부분의 경우 배치표준은 최적화 프로세스를 돕기 위해 추가됩니다(때로는 예측 성능에도 도움이 될 수 있음). 배치표준을 사용하는 모델은 훈련을 완료하는 데 더 적은 epoch가 필요한 경향이 있습니다. 게다가, 배치노드는 훈련이 "멈추게" 만들 수 있는 다양한 문제를 해결할 수도 있습니다. 특히 훈련 중에 문제가 있는 경우 모델에 배치 정규화를 추가하는 것을 고려해보세요.

방법 5: L1 및 L2 정규화

L1 및 L2 정규화는 신경망에서 큰 가중치에 페널티를 적용하여 과적합을 방지하는 데 사용되는 기술입니다. L1 정규화는 가중치의 절대값에 비례하는 손실 함수에 페널티 항을 추가합니다. 이는 가중치의 희소성을 장려하고 기능 선택으로 이어질 수 있습니다. 가중치 감소라고도 알려진 L2 정규화는 손실 함수에 가중치의 제곱에 비례하는 페널티 항을 추가합니다. 이는 가중치가 너무 커지는 것을 방지하고 가중치 분포가 보다 고르게 분산되도록 장려합니다.

L1 정규화와 L2 정규화 사이의 선택은 종종 특정 문제와 모델의 원하는 속성에 따라 달라집니다.

L1/L2 정규화 값이 크면 모델이 빠르게 학습하지 못하고 학습 정체기에 도달하여 모델이 과소적합됩니다. 

방법6: 데이터 확대

기계 학습 모델의 성능을 향상시키는 가장 좋은 방법은 더 많은 데이터를 학습하는 것입니다. 모델이 학습해야 하는 사례가 많을수록 이미지의 차이가 중요한 것과 그렇지 않은 것을 더 잘 인식할 수 있습니다. 데이터가 많을수록 모델이 더 잘 일반화되는 데 도움이 됩니다.

더 많은 데이터를 얻는 쉬운 방법 중 하나는 이미 가지고 있는 데이터를 사용하는 것입니다. 클래스를 보존하는 방식으로 데이터세트의 이미지를 변환할 수 있다면(예: MNIST 숫자 분류를 증강 6으로 시도하면 6과 9를 구별하기 어려울 것임) 분류기에 이러한 종류의 변환을 무시하도록 가르칠 수 있습니다. 예를 들어, 자동차가 사진에서 왼쪽을 향하고 있는지 아니면 오른쪽을 향하고 있는지 여부는 그것이 트럭이 아니라 자동차라는 사실을 바꾸지 않습니다. 따라서 뒤집힌 이미지로 훈련 데이터를 강화하면 분류자는 "왼쪽 또는 오른쪽"이 무시해야 하는 차이임을 학습하게 됩니다.

이것이 데이터 증대의 기본 아이디어입니다. 실제 데이터와 상당히 유사해 보이는 추가 가짜 데이터를 추가하면 분류기가 향상됩니다. 

과적합을 방지하는 열쇠는 모델이 잘 일반화되는지 확인하는 것입니다. 항상 훈련 세트뿐만 아니라 검증 세트에서 모델 성능을 확인하세요.

데이터를 이용한 위 방법의 구현

위 방법의 구현 단계를 살펴보겠습니다.

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)

위의 그래프에서 모델이 과적합되었음을 분명히 알 수 있으므로 Drop out 정규화 및 Batch 정규화라는 또 다른 방법을 사용합니다.

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)

학습 그래프에서 배치 정규화 및 드롭아웃 레이어를 사용해도 모델이 과적합되는 것을 볼 수 있습니다. 따라서 복잡성을 증가시키는 대신 필터 수를 늘리십시오. 더 많은 특징을 추출하기 위해 더 많은 컨볼루션 레이어를 추가할 것입니다.

11단계: 컨볼루션 레이어 늘리기

훈련 가능한 매개변수를 줄이되 더 많은 특징을 추출하려면 컨볼루션 레이어를 늘리세요.

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의 제한된 교육 데이터에 대한 과적합 위험을 줄입니다.

spot_img

최신 인텔리전스

spot_img