들어가며

지난 글에서는 모델을 구축하기 위한 주택 관련 데이터들을 다운로드하고 간단하게 데이터를 살펴보고 어떻게 데이터를 다룰지 알아보았습니다. 이번에는 데이터를 훈련 세트와 테스테 세트로 나누는 다양한 방법과 장단점에 대해서 설명하도록 하겠습니다. 

2.1 테스트 세트를 만드는 이유

모델에 데이터를 훈련시키기 전에 주어진 데이터에서 테스트 세트를 추출하는 이유는 두 가지입니다.

첫째, 훈련 데이터로 학습된 모델을 평가하기 위해서입니다. 훈련 모델로만 학습된 모델이 얼마나 일반화되어있는지 평가하기 위해서는 훈련 데이터와 다른 데이터가 필요합니다. 

둘째, 데이터 스누핑 편향(data snooping bias)을 방지하기 위해서 입니다.  만일 개발자가 모든 데이터를 이용해서 모델을 학습시키고 평가하여 정확도를 높이기 위해 신경망 모델을 임의로 계속 선택한다면 해당 데이터에 대해서만 최적화를 진행하게 되어 모델이 과대 적합이 될 수 있는데 이는 데이터 스누핑 편향을 초래합니다. 하지만 테스트 세트를 때어내서 훈련에 사용하지 못하도록 하면 데이터 스누핑 현상을 방지할 수 있습니다.

2.2 테스트 세트 만들기

본격적으로 테스트 세트를 만들어 봅시다. 테스트 세트의 비율은 20%라고 정하고 진행하도록 하겠습니다.

2.2.1 랜덤 테스트 세트 추출

가장 간단한 방법은 전체 데이터에서 20%를 랜덤으로 추출하는 방법입니다.

np.random.permutation(len(data))을 이용해서 20%의 인덱스를 추출하여 이를 이용해서 간단하게 훈련, 테스트 세트로 나눌 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
 
# 예시를 위해서 만든 것입니다. 사이킷런에는 train_test_split() 함수가 있습니다.
# np.random.permutation 인수만큼 순열 데이터를 만듬
def split_train_test(data, test_ratio):
    shuffled_indices = np.random.permutation(len(data))
    test_set_size = int(len(data) * test_ratio)
    test_indices = shuffled_indices[:test_set_size]
    train_indices = shuffled_indices[test_set_size:]
    return data.iloc[train_indices], data.iloc[test_indices]
cs

https://colab.research.google.com/drive/1wWaPMCEb3ewf3CNKIeu262vQaCG36apw#scrollTo=zA9zK_EJsfgK&line=3&uniqifier=1

 

Google Colaboratory

 

colab.research.google.com

그러나

만약 이를 여러 번 계속할 경우 테스트 세트와 훈련 세트는 달라지므로 이는 결국 전체 데이터를 훈련 데이터에 사용하게 되므로 데이터 스누핑 편향을 초래합니다.

이를 방지하기 위해서는 두 가지 방법이 있습니다.

2.2.2 랜덤 추출_유사 난수 초기화 테스트 세트 추출

 

1
2
3
4
5
6
7
8
9
10
11
# 일관된 출력을 위해 유사난수 초기화
np.random.seed(42)
 
# 예시를 위해서 만든 것입니다. 사이킷런에는 train_test_split() 함수가 있습니다.
# np.random.permutation 인수만큼 순열 데이터를 만듬
def split_train_test(data, test_ratio):
    shuffled_indices = np.random.permutation(len(data))
    test_set_size = int(len(data) * test_ratio)
    test_indices = shuffled_indices[:test_set_size]
    train_indices = shuffled_indices[test_set_size:]
    return data.iloc[train_indices], data.iloc[test_indices]
cs

유사 난수를 초기화하면 매번 실행해도 같은 세트가 추출되므로 이를 방지할 수 있습니다. 그러나 만약 데이터셋 자체가 업데이트되는 경우 shuffled_indices의 값 자체가 변경되어 기존의 훈련 세트가 테스트 세트로 이용될 가능성이 있습니다. 이를 방지하기 위해서 샘플의 고유 식별자(id)를 이용한 HASH계산을 통해서 추출하는 방법이 있습니다.

2.2.3 해쉬를 이용한 테스트 세트 추출

해쉬를 사용하는 방법은 간단합니다. 각 식별자 데이터의 마지막 바이트 값이 51보다 낮은 경우(256의 20%) 테스트 세트에 포함시키고 나머지는 훈련 세트에 포함시키는 것입니다. 이것으로 완전히 데이터 스누핑 편향을 방지하는 추출 방식을 구현할 수 있습니다. 아래는 이를 구현한 코드입니다.

1
2
3
4
5
6
7
8
9
from zlib import crc32
#crc32를 통해서 파이썬 2,3 모두 지원할 수 있습니다.
def test_set_check(identifier, test_ratio):
    return crc32(np.int64(identifier)) & 0xffffffff < test_ratio * 2**32
 
def split_train_test_by_id(data, test_ratio, id_column):
    ids = data[id_column]
    in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio))
    return data.loc[~in_test_set], data.loc[in_test_set]
cs

또는

1
2
3
4
import hashlib
 
def test_set_check(identifier, test_ratio, hash=hashlib.md5):
    return bytearray(hash(np.int64(identifier)).digest())[-1< 256 * test_ratio
cs

결과

https://colab.research.google.com/drive/1wWaPMCEb3ewf3CNKIeu262vQaCG36apw#scrollTo=YdkFgltcLGGn

 

Google Colaboratory

 

colab.research.google.com

2.2.4 사이킷런을 이용한 테스트 세트 추출

지금까지 직접 테스트 세트를 추출하는 방법을 설명했습니다. 사이킷런에서는 이를 일일이 코딩하지 않고 추출할 수 있도록 모듈을 지원합니다.

 

마치며 

이번에는 데이터를 테스트 세트와 훈련 세트로 분할하는 방법에 대해서 알아보았습니다. 데이터 스누핑 현상을 피하기 위해서 마지막으로 해쉬를 이용한 테스트 세트 추출방식을 사용했지만 사실 이를 사용한 이유는 한 가지 더 있습니다. 이는 수집되는 데이터의 특성이 시간에 따라 변하는 것을 고려하기 위해서입니다. 왜 그런지에 대해서 알아보시면 더 잘 이해하 실수 있으실 겁니다. 다음 시간에는 좀 더 깊게 데이터를 탐구하는 방법에 대해서 진행하겠습니다.

+ Recent posts