반응형

machinelearningmastery.com의 []의 내용임.


How to Reduce Overfitting With Dropout Regularization in Keras

    [Dropout regularization]은 심층신경망(deep neural network)를 정규화하기 위한 저렴한 방법이다.

    드롭아웃은 확률적 제거 또는 레이어에 입력을 삭제하는 것에의해 동작한다. 이는 데이터 샘플 또는 이전 레이어로부터의 활성화내의 입력 변수일 것이다. 드롭아웃은 매우 다른 네트워크를 가진 대규모 네트워크의 시뮬레이션 효과를 갖는다. 그래서 결국 일반적으로 입력에 대해 더 강한 네트워크내 노드를 만든다.

    이 글에서는 심층신경망 모델에 드롭아웃 정규화를 추가하는 Keras API에 대해 다룰 것이다.

    이 글을 읽고 나면, 아래와 같은 내용을 알게 될 것이다,

  • 케라스 API를 사용하여 드롭아웃 레이어를 생성하는 방법
  • 케라스 API를 사용하여 MLP, CNN, RNN 레이어에 드롭아웃 정규화를 추가하는 방법
  • 기존 모델에 드롭아웃 정규화를 추가하여 오버피팅을 줄이는 방법

Tutorial Overview

  1. Dropout Regularization in Keras
  2. Dropout Regularization on Layers
  3. Dropout Regularization Case Study

Dropout Regularization in Keras

    케라스는 드롭아웃 정규화를 지원한다.

    케라스에서 드롭아웃의 가장 간단한 형태는 Dropout core layer로 제공되는 것이다.

    생성시, 드롭아웃 비율(dropout rate)을 레이어에 각 입력을 설정하는 것의 확률로써 0으로 레이어에 지정할 수 있다. 이는 논문에서 드롭아웃 비율의 정의와는 다르다. 케라스에서 드롭아웃 비율은 입력을 유지하는 확률을 나타낸다.

    따라서, 논문에서의 드롭아웃 비율이 0.8일 때, 케라스에서는 0.2의 드롭아웃 비율이 된다.(20%의 입력을 0으로 설정)

    아래는 50%의 확률로 입력이 0으로 설정되는 드롭아웃 레이어를 생성하는 예제이다.

     layer = Dropout(0.5)


Dropout Regularization on Layers

    드롭아웃 레이어는 레이어 사이에 추가되어 지고 이전 레이어의 출력에 적용되고 연이은 레이어로 전달된다.

    예를 들어 두개의 dense 레이어가 주어지면,


    model.append(Dense(32))
    model.append(Dense(32))

Dense 레이어 사이에 드롭아웃 레이어를 삽입할 수 있다. 이 경우, 첫번째 레이어의 출력 또는 활성화(activation)가 해당계층에 적용된 드롭아웃을 가지고 있고 다음 레이어의 입력으로 전달된다.

    아래 예제는 두번째 레이어에 드룹아웃이 적용된 것이다.


    model.append(Dense(32))
    model.append(Dropout(0.5))
    model.append(Dense(32))

    드롭아웃은 시각적(visible) 레이어 즉 테트워크에 입력에 적용될 수 있다.

    이는 첫번째 레이어로써 드롭아웃 레이어를 가진 테트워크를 정의하고 입력 샘플의 예상되는 모양을 저정하기 위한 input_shape인자를 추가해야만 한다.


    model.add(Dropout(0.5, input_shape=(2,)))

    이제 어떻게 드롭아웃 정규화가 일반적인 네트워크와 사용되는지를 살펴보자.


MLP Dropout Regularization

    아래는 두개의 dense fully connected layer사이에 dropout을 추가한 예제이다.


    # example of dropout between fully connected layers
    from keras.layers import Dense
    from keras.layers import Dropout
    ...
    model.add(Dense(32))
    model.add(Dropout(0.5))
    model.add(Dense(1))
    ...

CNN Dropout Regularization

    드롭아웃은 합성곱 레이어(convolutional layer)와 풀링 레이어 다음에 사용될 수 있다.

    자주, 드롭아웃만 풀링레이어 이후 사용되기도 하지만, 이는 오로지 경험적인 것이다.


    from keras.layers import Dense
    from keras.layers import Conv2D
    from keras.layers import MaxPooling2D
    from keras.layers import Dropout
    ...
    model.add(Conv2D(32, (3,3)))
    model.add(Conv2D(32, (3,3)))
    model.add(MaxPooling2D())
    model.add(Dropout(0.5))
    model.add(Dense(1))
    ...

    이 경우, 드롭아웃은 각 요소 또는 특성맵내의 셀(cell)에 적용된다.

    합성곱 신경망과 드롭아웃을 사용하기 위한 다른 방법은 합성곱 레이어로부터의 전체 특성맵을 제거(dropout)하는 것이다. 그러면 풀링동안 사용되지 않는다. 이 방법을 spatial dropout 또는 SpatialDropout이라고 부른다.

    Spatial Dropout은 SpatialDropout2D(1D, 3D도 있다)로 케라스에서 제공된다.


    from keras.layers import Dense
    from keras.layers import Conv2D
    from keras.layers import MaxPooling2D
    from keras.layers import SpatialDropout2D
    ...
    model.add(Conv2D(32, (3,3)))
    model.add(Conv2D(32, (3,3)))
    model.add(SpatialDropout2D(0.5))
    model.add(MaxPooling2D())
    model.add(Dense(1))
    ...

RNN Dropout Regularization

    다음은 LSTM 재귀 레이어와 dense fully connected layer 사이에 드롭아웃을 추가한 예제이다.


    # example of dropout between LSTM and fully connected layers
    from keras.layers import Dense
    from keras.layers import LSTM
    from keras.layers import Dropout
    ...
    model.add(LSTM(32))
    model.add(Dropout(0.5))
    model.add(Dense(1))
    ...

    이 예제는 Dense 레이어의 입력으로 제공된 LSTM 레이어로부터 32개의 출력에 드롭아웃을 적용한다.

    대신, LSTM으로의 입력이 드롭아웃을 당할 수도 있다. 이 경우, 다른 드롭아웃 마스크(mask)가 LSTM으로 제공되는 각 샘플에서 매 step마다 적용된다.

mask(zeros and ones) [Dropout Layer]


    # example of dropout before LSTM layer
    from keras.layers import Dense
    from keras.layers import LSTM
    from keras.layers import Dropout
    ...
    model.add(Dropout(0.5, input_shape=(...)))
    model.add(LSTM(32))
    model.add(Dense(1))
    ...

    LSTM같은 재귀적 레이어와 드롭아웃을 사용하기 위한 다른 방법도 있다. LSTM은 샘플래 모든 입력에 대해 동일한 드롭아웃 마스크를 사용할 수 있다. 샘플의 시간단계에 걸쳐 재귀적인 입력 연결에 대해서도 동일한 접근을 사용할 수 있다. 재귀 모델에서 드롭아웃을 하는 이러한 접근을 Variational RNN이라고 한다.

Variational RNN은 재귀 레이어를 포함하여 매 단계에서 동일한 드롭아웃 마스크를 사용한다. ...

- A Theoretically Grounded Application of Dropout in Recurrent Neural Networks, 2016.

    케라스는 Variational RNNs(즉, 입력과 재귀 입력에 대한 샘플의 시간단계에 걸쳐 일관된 드롭아웃)을 재귀레이어의 입력을 위한 dropout과 재귀 입력을 위한 recurrent_dropout두개 인자를 통해 지원한다.


    # example of variational LSTM dropout
    from keras.layers import Dense
    from keras.layers import LSTM
    from keras.layers import Dropout
    ...
    model.add(LSTM(32, dropout=0.5, recurrent_dropout=0.5))
    model.add(Dense(1))
    ...


Dropout Regularization Case Study

    단순 이진 분류 문제에서 MLP(Multi Layer Perceptron)의 오버피팅을 줄이기 위한 드롭아웃 정규화를 사용하는 방법을 보자.

     예제는 분류와 회귀 문제를 위한 신경망에 드롭아웃 정규화를 적용하기 위한 템플릿을 제공한다.

Binary Classification Problem

    여기서는 관측치의 2차원 동심원 두개를 정의하는 표준 이진 분류 문제를 사용한다. 여기서 원 하나는 하나의 분류(class)이다.

    각 관측치는 동일 스케일이고 0아니면 1의 출력값을 갖는 두개의 변수를 갖는다. 이 데이터셋은 그림으로 그렸을 때, 각 분류에서의 관측치의 모양 때문에 '원(circle)' 데이터셋으로 불린다.

    관측치를 생성하기 위해 make_circles()함수를 사용한다. 그리고 데이터에 노이즈를 추가하고 코드가 실행하는 매번 동일한 샘플이 생성되도록 random number generator를 제공(seed)한다.


    # generate 2d classification dataset
    X, y = make_circles(n_samples=100, noise=0.1, random_state=1)

    그래프에 두 변수가 x,y 좌표로 주어진 데이터셋을 그래프로 그리고 주어진 분류 값은 관측치의 색으로 그릴 수 있다.

    아래 예제는 데이터셋을 생성하고 그리는 완전한 코드이다.


    # generate two circles dataset
    from sklearn.datasets import make_circles
    from matplotlib import pyplot
    from pandas import DataFrame
    # generate 2d classification dataset
    X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
    # scatter plot, dots colored by class value
    df = DataFrame(dict(x=X[:,0], y=X[:,1], label=y))
    colors = {0:'red', 1:'blue'}
    fig, ax = pyplot.subplots()
    grouped = df.groupby('label')
    for key, group in grouped:
        group.plot(ax=ax, kind='scatter', x='x', y='y', label=key, color=colors[key])
    pyplot.show()

    예제를 실행하면 각 클래스에 관측치가 동심원 모양으로 보이는 산포도(scatter plot)를 그린다. 덜 분명한 원을 만드는 점의 분포에서 노이즈를 확인할 수 있다.



Scatter Plot of Circles Dataset with Color Showing the Class Value of Each Sample

    이것은 분류가 선으로 구분될수 없기 때문에 훌륭한 테스트 문제이다. 예를 들면, 선으로 구분가능하지 않으며, 다루기 위해 신경망 같은 비선형 방법이 필요한 것이다.

    신경망에는 부족한 단 100개의 샘플만 생성하여 훈련 데이터셋이 오버피팅 될 수 있도록 하고 테스트 데이터세에서 높은 오류를 갖게한다. 이는 정규화 사용을 위한 좋은 케이스이다. 게다가 샘플이 노이즈를 포함하고 있어 모델에 생성하지 않은 샘플을 학습할 수 있는 기회를 제공한다.

Overfit Multilayer Perceptron

    이진 분류 문제를 다루기 위해 MLP 모델을 만든다.

    모델은 이 문제를 해결하기 위해 필요한 것보다 더 많은 노드를 갖는 히든 레이어 하나를 갖는다. 이는 오버피팅이 잘 일어나게 한다. 또한 모델이 필요한 것보다 더 길게 훈련하여 오버피팅을 확실하게 만든다.

    모델을 정의하기 전에 데이터셋을 훈련을 위해 30, 평가를 위해 70으로 나눈다.


    # generate 2d classification dataset
    X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
    # split into train and test
    n_train = 30
    trainX, testX = X[:n_train, :], X[n_train:, :]
    trainy, testy = y[:n_train], y[n_train:]

    다음으로 모델을 정의한다.

    히든레이어는 500노드를 갖고 relu (rectified linear activation funtion)을 사용한다. 시그모이드 함수는 0 또는 1의 분류값을 예측하기 위해 출력 레이어에 사용한다.

    모델은 이진 분류에 적합한 binary cross entropy 손실함수와 효과적인 Adam 옵티마이져를 사용하여 최적화 한다.


    # define model
    model = Sequential()
    model.add(Dense(500, input_dim=2, activation='relu'))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

    기본 배치 크기를 32로 하고 4,000번 훈련한다.

    또한 검증 데이터셋으로 테스트 데이터셋을 사용한다.


    # fit model
    history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0)

    테스트 데이터셋에어 모델의 성능을 평가하고 결과를 출력한다.


    # evaluate the model
    _, train_acc = model.evaluate(trainX, trainy, verbose=0)
    _, test_acc = model.evaluate(testX, testy, verbose=0)
    print('Train: %.3f, Test: %.3f' % (train_acc, test_acc))

    마지막으로 각 epoch마다 훈련과 테스트셋에 대한 모델의 성능을 그래프로 그린다.

    만약 모델이 정말로 훈련 데이터셋에 오버피팅되었다면, 모델이 훈련 데이터셋에서 통계적 노이즈를 학습함으로써 훈련 셋에서의 정확도 곡선이 계속 증가하고 테스트 셋에서는 증가하고 다시 떨어질 것을 예상할 수 있다.


    # plot history
    pyplot.plot(history.history['accuracy'], label='train')
    pyplot.plot(history.history['val_accuracy'], label='test')
    pyplot.legend()
    pyplot.show()

    완성된 예제 소스는 아래와 같다.


    # mlp overfit on the two circles dataset
    from sklearn.datasets import make_circles
    from keras.layers import Dense
    from keras.models import Sequential
    from matplotlib import pyplot
    # generate 2d classification dataset
    X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
    # split into train and test
    n_train = 30
    trainX, testX = X[:n_train, :], X[n_train:, :]
    trainy, testy = y[:n_train], y[n_train:]
    # define model
    model = Sequential()
    model.add(Dense(500, input_dim=2, activation='relu'))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    # fit model
    history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0)
    # evaluate the model
    _, train_acc = model.evaluate(trainX, trainy, verbose=0)
    _, test_acc = model.evaluate(testX, testy, verbose=0)
    print('Train: %.3f, Test: %.3f' % (train_acc, test_acc))
    # plot history
    pyplot.plot(history.history['accuracy'], label='train')
    pyplot.plot(history.history['val_accuracy'], label='test')
    pyplot.legend()
    pyplot.show()

    예제를 실행하면 훈련과 테스트 데이터셋에서 모델 성능을 보여준다.

    모델이 훈련 데이터셋이 테스트 데이터셋보다 더 좋은 성능을 갖는 것을 볼 수 있다. 이는 오버피팅의 한가지 신호이다.

    통계적 특징과 훈련 알고리즘에 따라 결과는 다양하게 나타난다. 모델이 심하게 오버피팅되었기 때문에 보통 많은 것을 기대하지 않는다. 만약에 있다면, 동일 데이터셋에서 모델의 반복 실행에 걸친 정화도의 변량이다.


    Train: 1.000, Test: 0.757



Line Plots of Accuracy on Train and Test Datasets While Training Showing an Overfit

    위 그림에서 테스트 정확도가 한 지점까지 증가하고 다시 감소하기 시작하는 기대했던 오퍼피팅된 모델을 확인할 수 있다.

Overfit MLP With Dropout Regularization

    드롭아웃 정규화를 사용하기 위해 예제를 수정하자.

    이 작업은 히든 레이어와 출력 레이어 사이에 새로운 드롭아웃 레이어를 추가하는 것으로 간단하게 수정할 수 있다. 이 경우, 드롭아웃 비율(히든레이어로부터의 출력을 0으로 설정할 가능성)을 40% 또는 0.4로 지정한다.


    # define model
    model = Sequential()
    model.add(Dense(500, input_dim=2, activation='relu'))
    model.add(Dropout(0.4))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

    히든레이어 다음에 드롭아웃을 추가한 완전한 샘플은 아래와 같다.


    # mlp with dropout on the two circles dataset
    from sklearn.datasets import make_circles
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers import Dropout
    from matplotlib import pyplot
    # generate 2d classification dataset
    X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
    # split into train and test
    n_train = 30
    trainX, testX = X[:n_train, :], X[n_train:, :]
    trainy, testy = y[:n_train], y[n_train:]
    # define model
    model = Sequential()
    model.add(Dense(500, input_dim=2, activation='relu'))
    model.add(Dropout(0.4))
    model.add(Dense(1, activation='sigmoid'))
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    # fit model
    history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0)
    # evaluate the model
    _, train_acc = model.evaluate(trainX, trainy, verbose=0)
    _, test_acc = model.evaluate(testX, testy, verbose=0)
    print('Train: %.3f, Test: %.3f' % (train_acc, test_acc))
    # plot history
    pyplot.plot(history.history['accuracy'], label='train')
    pyplot.plot(history.history['val_accuracy'], label='test')
    pyplot.legend()
    pyplot.show()

    위 예제를 실행하면 훈련 및 테스트 데이터셋에서의 모델 성능을 보여준다.

    결과는 아마 다양할 것이다. 이 경우, 모델의 결과는 높은 변화량을 갖는다.

    아래 실행 결과에서 훈련 데이터셋은 정확도가 100%에서 96%로 약간 떨어지고 테스트셋에서는 정확도가 75%에서 81%로 약간 올라간 것을 볼 수 있다.

Train: 0.967, Test: 0.814

    아래 그림에서 훈련동안의 훈련 및 테스트 정확도를 보면, 모델이 훈련 데이터셋을 오버피팅하는 것이 더 이상 나타나지 않는 것을 볼 수 있다.

    훈련과 테스트 셋에서의 모델 정확도는 비록 훈련동안 드롭아웃을 사용하여 많은 노이즈가 있지만, 증가하고 안정화된다.

반응형

+ Recent posts