도순씨의 코딩일지

딥러닝 :: 과적합 피하기 본문

1988년 존스홉킨스대학교의 세즈노프스키(Sejnowski)교수는 오차 역전파 알고리즘을 사용한 신경망이 과연 얼마나 광석과 돌을 구분하는 데 효과적인지 알아보려 했다. 그때나 지금이나 어렵게 해결하는 과적합(overfitting) 문제는 어떻게 해결해야 좋을까? 모델이 과적합되면 훈련 데이터에 대한 정확도는 높을 수 있지만 검증 데이터에 대한 정확도는 낮아진다. 따라서 과적합을 막을 필요성이 있다.

 

@ 데이터는 https://github.com/gilbutITbook/006958 여기서 다운로드 받으실 수 있습니다.

 

 

1. 데이터 확인과 실행


sonar.csv 파일을 사용하여 확인해보도록 하자.

 

@ 코드

1
2
3
4
import pandas as pd
 
df = pd.read_csv("/Users/Downloads/006958-master/deeplearning/dataset/sonar.csv")
print(df.info())
cs

 

@ 실행 결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 207 entries, 0 to 206
Data columns (total 61 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   0.0200  207 non-null    float64
 1   0.0371  207 non-null    float64
 2   0.0428  207 non-null    float64
 3   0.0207  207 non-null    float64
 4   0.0954  207 non-null    float64
 5   0.0986  207 non-null    float64
(생략)
 49  0.0324  207 non-null    float64
 50  0.0232  207 non-null    float64
 51  0.0027  207 non-null    float64
 52  0.0065  207 non-null    float64
 53  0.0159  207 non-null    float64
 54  0.0072  207 non-null    float64
 55  0.0167  207 non-null    float64
 56  0.0180  207 non-null    float64
 57  0.0084  207 non-null    float64
 58  0.0090  207 non-null    float64
 59  0.0032  207 non-null    float64
 60  R       207 non-null    object 
dtypes: float64(60), object(1)
memory usage: 98.8+ KB
None
cs

모든 컬럼이 실수형(float64)인데 맨 마지막 컬럼만 객체형인 것을 확인할 수 있다. 이를통해 마지막에 나오는 컬럼은 클래스이며 데이터형 변환이 필요한 것을 알 수 있다. 

 

전체 코드는 다음과 같다.

 

@ 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from keras.models import Sequential
from keras.layers.core import Dense
from sklearn.preprocessing import LabelEncoder
 
import pandas as pd
import numpy
import tensorflow as tf
 
# 시드값 설정
seed = 0
numpy.random.seed(seed)
tf.random.set_seed(seed)
 
# 데이터 입력
df = pd.read_csv("/Users/hansubin/Downloads/006958-master/deeplearning/dataset/sonar.csv", header = None)
 
dataset = df.values
= dataset[:, 0:60]
Y_obj = dataset[:, 60]
 
# 문자열 변환
= LabelEncoder()
e.fit(Y_obj)
= e.transform(Y_obj)
 
# 모델 설정
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 = 'mean_squared_error', optimizer = 'adam', metrics = ['accuracy'])
 
# 모델 실행
model.fit(X, Y, epochs = 200, batch_size = 5)
 
# 결과 출력
print("\n Accuracy : %.4f" %(model.evaluate(X, Y)[1]))
cs

 

@ 실행 결과

1
 Accuracy : 0.9856
cs

정확도가 98.56%라는 것을 알 수 있다.

 

 

 

2. 과적합 이해하기 


과적합(over fitting)이란 앞에서 언급한 것처럼 모델이 학습 데이터셋 안에서는 일정 수준 이상의 예측 정확도를 보이지만, 새로운 데이터에 적용하면 잘 맞지 않는 것을 말한다. 과적합은 층이 너무 많거나 변수가 복잡해서 발생하기도 하고 테스트 셋과 학습 셋이 중복될 때 생기기도 한다. 

 

 

 

3. 학습셋과 테스트셋


그렇다면 과적합을 방지하기 위한 방법으로는 무엇을 해야할까? 

먼저 학습을 하는 데이터셋과 테스트 셋을 구분한 다음 학습과 동시에 테스트를 진행하는 방법이 있다. 예를 들면 다음과 같은 방식으로 진행할 수 있다. 신경망을 만들어 70개의 샘플로 학습을 진행 한 후 이 학습의 결과를 저장한다. 이렇게 저장된 파일을 '모델'이라고 부른다. 모델은 다른 셋에 적용할 경우 학습 단계에서 각인되었던 그대로 수행한다. 따라서 나머지 샘플로 정확도를 살펴보면 학습이 얼마나 잘 되었는지를 알 수 있다. 만약 테스트 셋을 만들지 않고도 정확도를 계산할 수 있었다면, 데이터에 들어있는 모든 샘플들을 그대로 테스트에 활용한 결과이다. 

학습 셋만 가지고 평가하면 층을 더하거나 에포크(epoch) 값을 높여 실행 횟수를 늘리면 정확도가 계속해서 올라갈 순 있다. 하지만 이러한 예측 성공률이 테스트셋에서 그대로 나타나는 것은 아니다. 

 

은닉층 수가 올라감에 따라 학습셋의 예측률과 테스트의 예측률이 어떻게 변하는지 정리하면 다음과 같다.

은닉층 수의 변화 학습셋의 예측률 테스트셋의 예측률
0 79.3 73.1
2 96.2 85.7
3 98.1 87.6
6 99.4 89.3
12 99.8 90.4
24 100 89.2

 

은닉층이 늘어날수록 학습셋의 예측률은 높아지지만 테스트셋의 예측률은 점점 올라가다가 줄어든다. 식이 복잡해지고 학습량이 늘어날수록 학습셋 예측률은 높아지지만 테스트셋은 그렇지 않을 수 있다는 것이다.

 

그렇다면 테스트셋과 학습셋은 어떻게 나눌까? sklearn 라이브러리의 train_test_split() 함수를 사용하면 된다. 이 함수는 불러온 X 데이터와 Y 데이터에서 각각 정해진 비율(%)으로 구분하여 한 그룹은 학습에 사용하고 다른 한 그룹은 테스트에 사용할 수 있다. 다음 예시는 학습셋을 70%, 테스트셋을 30%로 설정한 것이다.

1
2
3
4
from sklearn.model_selection import train_test_split
 
# 학습셋과 테스트셋의 구분
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.3, random_state = seed)
cs

 

그리고 모델을 실행하는 부분에서 위에서 만들어진 학습셋으로 학습을, 테스트셋으로 테스트를 하게 되려면 다음과 같이 실행한다.

1
2
3
4
5
model.fit(X_train, Y_train, epochs = 130, batch_size = 5)
 
 
# 테스트셋에 모델 적용
print("\n Test Accuracy: %.4f" %(model.evaluate(X_test, Y_test)[1]))
cs

 

전체 코드는 다음과 같다.

 

@ 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from keras.models import Sequential
from keras.layers.core import Dense
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
 
import pandas as pd
import numpy
import tensorflow as tf
 
# 시드값 설정
seed = 0
numpy.random.seed(seed)
tf.random.set_seed(seed)
 
# 데이터 입력
df = pd.read_csv("/Users/hansubin/Downloads/006958-master/deeplearning/dataset/sonar.csv", header = None)
 
dataset = df.values
= dataset[:, 0:60]
Y_obj = dataset[:, 60]
 
# 문자열 변환
= LabelEncoder()
e.fit(Y_obj)
= e.transform(Y_obj)
 
# 학습셋과 데스트셋의 구분
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.3, random_state = seed)
 
# 모델 설정
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 = 'mean_squared_error', optimizer = 'adam', metrics = ['accuracy'])
 
# 모델 실행
model.fit(X_train, Y_train, epochs = 200, batch_size = 5)
 
# 결과 출력
print("\n Accuracy : %.4f" %(model.evaluate(X_test, Y_test)[1]))
cs

 

@ 실행 결과

1
 Accuracy : 0.8254
cs

정확도가 82.54%이다. 앞서 100%에 가까운 것을 확률을 보였던 것과는 많이 다른 것을 알 수 있다.

 

 

4. 모델 저장과 재사용


테스트해 본 결과가 만족스러울 경우 저장하여 새로운 데이터에 학습할 수 있다.

1
2
3
from keras.models import load_model
 
model.save('my_model.h5')
cs
1
model = load_model('my_model.h5')
cs

 

전체 코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from keras.models import Sequential
from keras.models import load_model
from keras.layers.core import Dense
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
 
import pandas as pd
import numpy
import tensorflow as tf
 
# 시드값 설정
seed = 0
numpy.random.seed(seed)
tf.random.set_seed(seed)
 
# 데이터 입력
df = pd.read_csv("/Users/hansubin/Downloads/006958-master/deeplearning/dataset/sonar.csv", header = None)
 
dataset = df.values
= dataset[:, 0:60]
Y_obj = dataset[:, 60]
 
# 문자열 변환
= LabelEncoder()
e.fit(Y_obj)
= e.transform(Y_obj)
 
# 학습셋과 데스트셋의 구분
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.3, random_state = seed)
 
# 모델 설정
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 = 'mean_squared_error', optimizer = 'adam', metrics = ['accuracy'])
 
# 모델 실행
model.fit(X_train, Y_train, epochs = 130, batch_size = 5)
model.save('my_model.h5')
 
del model
model = load_model('my_model.h5')
 
# 결과 출력
print("\n Accuracy : %.4f" %(model.evaluate(X_test, Y_test)[1]))
cs

 

5. k겹 교차 검증


데이터가 충분하지 않다면 좋은 결과를 내기 어렵다. 이러한 단점을 보완하기 위해서 만든 것이 k겹 교차 검증이다. k겹 교차 검증은 데이터셋을 여러 개로 나누어 하나씩 테스트 셋으로 사용하고 나머지를 모두 합해서 학습셋으로 사용하는 방법이다. 이렇게 하면 가지고 있는 데이터 100%를 테스트셋으로 사용할 수 있다.

 

데이터를 원하는 숫자만큼 쪼개 각각 학습셋과 테스트셋으로 사용하게 만드는 sklearn의 StratifiedKFold()이다.

1
2
3
4
from sklearn.model_selection import StratifiedKFold
 
n_fold = 10
skf = StratifiedKFold(n_splits = n_fold, shuffle = True, random_state = seed)
cs

 

10번 쪼개고 싶다면 위와 같이 실행하면 된다.

위 코드는 10개의 파일로 쪼개 테스트하는 10-fold cross validation을 실시하도록 n_fold의 값을 10으로 설정한 후 StratifiedKFold() 함수에 적용한다. 

 

다음 모델은 for 구문으로 묶어 n_fold만큼 반복하게 한다.

 

1
2
3
4
5
6
7
for train, test in skf.split(X, 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 = 'mean_squared_error', optimizer = 'adam', metrics = ['accuracy'])
    model.fit(X[train], Y[train], epochs = 100, batch_size = 5)
cs


전체 코드는 다음과 같다.

 

@ 코드 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from keras.models import Sequential
from keras.layers.core import Dense
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold
 
import pandas as pd
import numpy
import tensorflow as tf
 
# 시드값 설정
seed = 0
numpy.random.seed(seed)
tf.random.set_seed(seed)
 
# 데이터 입력
df = pd.read_csv("/Users/hansubin/Downloads/006958-master/deeplearning/dataset/sonar.csv", header = None)
 
dataset = df.values
= dataset[:, 0:60]
Y_obj = dataset[:, 60]
 
# 문자열 변환
= LabelEncoder()
e.fit(Y_obj)
= e.transform(Y_obj)
 
# 10개의 파일로 쪼갬
n_fold = 10
skf = StratifiedKFold(n_splits = n_fold, shuffle = True, random_state = seed)
 
# 빈 accuracy 배열
accuracy = []
 
# 모델의 설정, 컴파일, 실행
for train, test in skf.split(X, 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 = 'mean_squared_error', optimizer = 'adam', metrics = ['accuracy'])
    model.fit(X[train], Y[train], epochs = 100, batch_size = 5)
    k_accuracy = "%.4f" %(model.evaluate(X[test], Y[test])[1])
    accuracy.append(k_accuracy)
 
# 결과 출력
print("\n %.f fold accuracy: " %n_fold, accuracy)
cs

 

@ 실행 결과

1
 10 fold accuracy:  ['0.7143', '0.6667', '0.7619', '0.9048', '0.8095', '0.9048', '0.9048', '0.8095', '0.9500', '0.8500']
cs

 

 

* 출처

조태호, 모두의 딥러닝(2017), 길벗

https://wikidocs.net/61374

 

위키독스

온라인 책을 제작 공유하는 플랫폼 서비스

wikidocs.net

 

 

Comments