[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.models
의load_model()
을 사용하면 Unknown initializer: GlorotUniform 라는 에러가 발생한다. -
이유는
tensorflow
의 keras와keras
는 엄연히 다르기 때문이다. -
나는
tensorflow
의 keras를 사용했으므로load_model()
역시tensorflow
를 사용해야 에러가 발생하지 않는다. -
또한
keras
의load_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