도순씨의 코딩일지
딥러닝 :: 과적합 피하기 본문
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
X = dataset[:, 0:60]
Y_obj = dataset[:, 60]
# 문자열 변환
e = LabelEncoder()
e.fit(Y_obj)
Y = 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
X = dataset[:, 0:60]
Y_obj = dataset[:, 60]
# 문자열 변환
e = LabelEncoder()
e.fit(Y_obj)
Y = 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
X = dataset[:, 0:60]
Y_obj = dataset[:, 60]
# 문자열 변환
e = LabelEncoder()
e.fit(Y_obj)
Y = 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 X = dataset[:, 0:60] Y_obj = dataset[:, 60] # 문자열 변환 e = LabelEncoder() e.fit(Y_obj) Y = 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), 길벗
'𝐂𝐎𝐌𝐏𝐔𝐓𝐄𝐑 𝐒𝐂𝐈𝐄𝐍𝐂𝐄 > 𝐃𝐄𝐄𝐏 𝐋𝐄𝐀𝐑𝐍𝐈𝐍𝐆' 카테고리의 다른 글
딥러닝 :: 선형 회귀 적용하기 (0) | 2020.08.12 |
---|---|
딥러닝 :: 베스트 모델 만들기 (0) | 2020.08.12 |
딥러닝 :: 다중 분류 문제, 상관도 그래프, 원핫코딩 (0) | 2020.08.10 |
딥러닝 :: 데이터 가공하기, 그래프로 표현하기 (0) | 2020.08.10 |
딥러닝 :: 모델 설계하기, 교차 엔트로피, 모델 실행하기 (0) | 2020.08.06 |