[Python] 모두의 딥러닝 - 04. 딥러닝 기본기 다지기[과적합]

Updated:

모두의 딥러닝 교재를 토대로 공부한 내용입니다.

실습과정에서 필요에 따라 코드나 이론에 대한 추가, 수정사항이 있습니다.


기본 세팅

import numpy as np
import pandas as pd

import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns

import warnings
%matplotlib inline
%config InlineBackend.figure_format = 'retina'

mpl.rc('font', family='NanumGothic') # 폰트 설정
mpl.rc('axes', unicode_minus=False) # 유니코드에서 음수 부호 설정

# 차트 스타일 설정
sns.set(font="NanumGothic", rc={"axes.unicode_minus":False}, style='darkgrid')
plt.rc("figure", figsize=(10,8))

warnings.filterwarnings("ignore")

13. 과적합 피하기

이번 챕터에서는 과적합 문제를 확인하고 이를 방지하는 방법을 알아본다.

13.1 과적합 문제

음파 탐지기 데이터 정보를 이용해 광물과 돌을 구분하는 딥러닝 모델을 만들어 보자.

df = pd.read_csv("deeplearning/dataset/sonar.csv", header=None)
df.head()
0 1 2 3 4 5 6 7 8 9 ... 51 52 53 54 55 56 57 58 59 60
0 0.0200 0.0371 0.0428 0.0207 0.0954 0.0986 0.1539 0.1601 0.3109 0.2111 ... 0.0027 0.0065 0.0159 0.0072 0.0167 0.0180 0.0084 0.0090 0.0032 R
1 0.0453 0.0523 0.0843 0.0689 0.1183 0.2583 0.2156 0.3481 0.3337 0.2872 ... 0.0084 0.0089 0.0048 0.0094 0.0191 0.0140 0.0049 0.0052 0.0044 R
2 0.0262 0.0582 0.1099 0.1083 0.0974 0.2280 0.2431 0.3771 0.5598 0.6194 ... 0.0232 0.0166 0.0095 0.0180 0.0244 0.0316 0.0164 0.0095 0.0078 R
3 0.0100 0.0171 0.0623 0.0205 0.0205 0.0368 0.1098 0.1276 0.0598 0.1264 ... 0.0121 0.0036 0.0150 0.0085 0.0073 0.0050 0.0044 0.0040 0.0117 R
4 0.0762 0.0666 0.0481 0.0394 0.0590 0.0649 0.1209 0.2467 0.3564 0.4459 ... 0.0031 0.0054 0.0105 0.0110 0.0015 0.0072 0.0048 0.0107 0.0094 R

5 rows × 61 columns

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 208 entries, 0 to 207
Data columns (total 61 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   0       208 non-null    float64
 1   1       208 non-null    float64
 2   2       208 non-null    float64
 3   3       208 non-null    float64
 4   4       208 non-null    float64
 5   5       208 non-null    float64
 6   6       208 non-null    float64
 7   7       208 non-null    float64
 8   8       208 non-null    float64
 9   9       208 non-null    float64
 10  10      208 non-null    float64
 11  11      208 non-null    float64
 12  12      208 non-null    float64
 13  13      208 non-null    float64
 14  14      208 non-null    float64
 15  15      208 non-null    float64
 16  16      208 non-null    float64
 17  17      208 non-null    float64
 18  18      208 non-null    float64
 19  19      208 non-null    float64
 20  20      208 non-null    float64
 21  21      208 non-null    float64
 22  22      208 non-null    float64
 23  23      208 non-null    float64
 24  24      208 non-null    float64
 25  25      208 non-null    float64
 26  26      208 non-null    float64
 27  27      208 non-null    float64
 28  28      208 non-null    float64
 29  29      208 non-null    float64
 30  30      208 non-null    float64
 31  31      208 non-null    float64
 32  32      208 non-null    float64
 33  33      208 non-null    float64
 34  34      208 non-null    float64
 35  35      208 non-null    float64
 36  36      208 non-null    float64
 37  37      208 non-null    float64
 38  38      208 non-null    float64
 39  39      208 non-null    float64
 40  40      208 non-null    float64
 41  41      208 non-null    float64
 42  42      208 non-null    float64
 43  43      208 non-null    float64
 44  44      208 non-null    float64
 45  45      208 non-null    float64
 46  46      208 non-null    float64
 47  47      208 non-null    float64
 48  48      208 non-null    float64
 49  49      208 non-null    float64
 50  50      208 non-null    float64
 51  51      208 non-null    float64
 52  52      208 non-null    float64
 53  53      208 non-null    float64
 54  54      208 non-null    float64
 55  55      208 non-null    float64
 56  56      208 non-null    float64
 57  57      208 non-null    float64
 58  58      208 non-null    float64
 59  59      208 non-null    float64
 60  60      208 non-null    object 
dtypes: float64(60), object(1)
memory usage: 99.2+ KB
  • 데이터는 샘플 208개, 60개의 피처와 타겟으로 구성되어 있다.

  • 위 데이터로 딥러닝 모델을 실행 해보자.

import tensorflow as tf
from sklearn.preprocessing import LabelEncoder
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense

# 시드 설정
np.random.seed(3)
tf.random.set_seed(3)

# 데이터 분리
dataset = df.values                # 타겟이 object 형태이므로 전체 행렬이 object 형태가 된다.
X = dataset[:,:60].astype(float)   # object 형태 사용시 에러
Y = dataset[:,60]

# 타겟 레이블 인코딩
encoder = LabelEncoder()
encoder.fit(Y)
Y_encoded = encoder.transform(Y)

# 모델 설정
model = Sequential()
model.add(Dense(24, input_dim=60, activation="relu"))
model.add(Dense(10, activation="relu"))
model.add(Dense(1, activation="sigmoid"))

# 모델 컴파일
model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"])

# 모델 실행
model.fit(X, Y_encoded, epochs=200, batch_size=5)

# 결과 출력
print("-"*100)
print(f"Accuracy: {model.evaluate(X, Y_encoded, verbose=0)[1]: .4f}")
Train on 208 samples
Epoch 1/200
208/208 [==============================] - 0s 2ms/sample - loss: 0.6825 - accuracy: 0.5673
Epoch 2/200
208/208 [==============================] - 0s 192us/sample - loss: 0.6522 - accuracy: 0.6635
Epoch 3/200
208/208 [==============================] - 0s 196us/sample - loss: 0.6283 - accuracy: 0.6971
...
Epoch 198/200
208/208 [==============================] - 0s 198us/sample - loss: 0.0022 - accuracy: 1.0000
Epoch 199/200
208/208 [==============================] - 0s 166us/sample - loss: 0.0022 - accuracy: 1.0000
Epoch 200/200
208/208 [==============================] - 0s 178us/sample - loss: 0.0024 - accuracy: 1.0000
----------------------------------------------------------------------------------------------------
Accuracy:  1.0000
  • 예측 정확도가 100%로 이 데이터셋에 과적합 되있다.

  • 교재는 Sequential()Dense()를 각각 keras.models, keras.layers.core로 불러온다.

  • 이 경우 피처 데이터 타입을 object를 사용해도 문제없으나 tensorflow로 로드하면 에러가 발생한다.

  • 다만 교재와 같이 실행하면 시드를 설정하여도 매번 결과가 바뀐다.

  • 교재와 값이 다른 것을 넘어서 실행마다 결과가 바뀌어 패키지를 수정하였다.

  • 구글링하면 global seed, operation seed를 모두 설정해야 해결 가능하다고 하는데 또 개인마다 좀 다르다고 한다.

  • 결과창이 너무 길어 직접 삭제해두었다.
  • 13.2 Train / Test

과적합을 피하기 위해 원래 데이터를 train과 test로 나누어 모델을 실행해보자.

from sklearn.model_selection import train_test_split

# 시드 설정
np.random.seed(0)
tf.random.set_seed(3)

# 데이터 분리
dataset = df.values                # 타겟이 object 형태이므로 전체 행렬이 object 형태가 된다.
X = dataset[:,:60].astype(float)   # object 형태 사용시 에러
Y = dataset[:,60]

# 타겟 레이블 인코딩
encoder = LabelEncoder()
encoder.fit(Y)
Y_encoded = encoder.transform(Y)

# train, test 분리
X_train, X_test, Y_train, Y_test = train_test_split(X, Y_encoded, test_size=0.3, random_state=3)

# 모델 설정
model = Sequential()
model.add(Dense(24, input_dim=60, activation="relu"))
model.add(Dense(10, activation="relu"))
model.add(Dense(1, activation="sigmoid"))

# 모델 컴파일
model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"])

# 모델 실행
model.fit(X_train, Y_train, epochs=130, batch_size=5)

# 결과 출력
print("-"*100)
print(f"Accuracy: {model.evaluate(X_test, Y_test, verbose=0)[1]: .4f}")
Train on 145 samples
Epoch 1/130
145/145 [==============================] - 0s 2ms/sample - loss: 0.6841 - accuracy: 0.5241
Epoch 2/130
145/145 [==============================] - 0s 171us/sample - loss: 0.6644 - accuracy: 0.5862
Epoch 3/130
145/145 [==============================] - 0s 157us/sample - loss: 0.6478 - accuracy: 0.6345
...
Epoch 128/130
145/145 [==============================] - 0s 175us/sample - loss: 0.0254 - accuracy: 1.0000
Epoch 129/130
145/145 [==============================] - 0s 171us/sample - loss: 0.0240 - accuracy: 1.0000
Epoch 130/130
145/145 [==============================] - 0s 182us/sample - loss: 0.0236 - accuracy: 1.0000
----------------------------------------------------------------------------------------------------
Accuracy:  0.7937
  • test에 대해서 정확도가 약 79.37%로 앞서 전체로 학습하고 평가 했을 때 보다 낮게 나타난다.

  • test에 대한 정확도도 높은 편이라 과적합을 피해 잘 적합한 것 같다.

  • 결과창이 너무 길어 직접 삭제해두었다.

13.3 모델 저장 / 재사용

학습이 끝난 후 테스트 한 결과가 만족스럽다면 모델을 저장해서 새로운 데이터에 사용 가능하다.

from keras.models import load_model

# 모델 저장
model.save("my_model.h5")

# 모델 삭제
del model

# 모델 로드
model = tf.keras.models.load_model("my_model.h5")

# 결과 출력
print("-"*100)
print(f"Accuracy: {model.evaluate(X_test, Y_test, verbose=0)[1]: .4f}")
Using TensorFlow backend.


----------------------------------------------------------------------------------------------------
Accuracy:  0.7937
  • 앞서 패키지에 대해 언급하였듯이 나는 Sequential()Dense()tensorflow에서 로드하였다.

  • 만약 kears.modelsload_model()을 사용하면 Unknown initializer: GlorotUniform 라는 에러가 발생한다.

  • 이유는 tensorflow의 keras와 keras는 엄연히 다르기 때문이다.

  • 나는 tensorflow의 keras를 사용했으므로 load_model()역시 tensorflow를 사용해야 에러가 발생하지 않는다.

  • 또한 kerasload_model() 사용시 str’ object has no attribute ‘decode’ keras 라는 에러가 발생한다면,

  • pip install h5py==2.10.0으로 h5py 패키지를 다운그레이드 해준다(3이상 버전에서는 에러).

13.4 교차 검증

이번엔 교차 검증을 위해 StratifiedKFold()를 사용할 것이다.

Stratifed K-Fold는 전체 데이터의 분포도를 반영해서 train과 test로 데이터를 나눈다.

참고: 머신러닝 완벽가이드 - 사이킷런 소개

from sklearn.model_selection import StratifiedKFold

# 시드 설정
seed = 0
np.random.seed(seed)
tf.random.set_seed(seed)

# 데이터 분리
dataset = df.values                # 타겟이 object 형태이므로 전체 행렬이 object 형태가 된다.
X = dataset[:,:60].astype(float)   # object 형태 사용시 에러
Y = dataset[:,60]

# 타겟 레이블 인코딩
encoder = LabelEncoder()
encoder.fit(Y)
Y_encoded = encoder.transform(Y)

# 교차 검증
accuracy = []
n_fold = 10
skf = StratifiedKFold(n_splits = n_fold, shuffle = True, random_state = seed)

for train_idx, test_idx in skf.split(X, Y_encoded):
    # 모델 설정
    model = Sequential()
    model.add(Dense(24, input_dim=60, activation="relu"))
    model.add(Dense(10, activation="relu"))
    model.add(Dense(1, activation="sigmoid"))

    # 모델 컴파일
    model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"])

    # 모델 실행
    model.fit(X[train_idx], Y_encoded[train_idx], epochs=100, batch_size=5)
    
    # 결과 저장
    K_accuracy = model.evaluate(X[test_idx], Y_encoded[test_idx], verbose=0)[1]
    accuracy.append(K_accuracy)
Train on 187 samples
Epoch 1/100
187/187 [==============================] - 0s 2ms/sample - loss: 0.6767 - accuracy: 0.5829
Epoch 2/100
187/187 [==============================] - 0s 179us/sample - loss: 0.6477 - accuracy: 0.6364
Epoch 3/100
187/187 [==============================] - 0s 180us/sample - loss: 0.6203 - accuracy: 0.7433
...
Epoch 98/100
188/188 [==============================] - 0s 264us/sample - loss: 0.0888 - accuracy: 0.9840
Epoch 99/100
188/188 [==============================] - 0s 208us/sample - loss: 0.0844 - accuracy: 0.9894
Epoch 100/100
188/188 [==============================] - 0s 233us/sample - loss: 0.0861 - accuracy: 0.9947
# 결과 출력
print(f"fold별 교차 검증 결과: {np.round(accuracy, 4)}")
print(f"{n_fold} 교차 검증 결과 평균: {np.mean(accuracy):.4f}")
fold별 교차 검증 결과: [0.7143 0.8095 0.8095 0.9048 0.7619 0.8095 0.8095 0.7619 0.9    0.85  ]
10 교차 검증 결과 평균: 0.8131
  • 10 fold 교차 검증 결과 평균 정확도가 약 81.31%로 비교적 높게 나타난 듯 하다.

  • 결과창이 너무 길어 직접 삭제해두었다.

Leave a comment