머신러닝 프로젝트 처음부터 끝까지
1. 큰 그림 보기
2. 데이터 구하기
3. 데이터로부터 인사이트를 얻기 위한 탐색 및 시각화
4. 머신러닝 알고리즘을 위한 데이터 준비
5. 모델 선택 및 훈련
6. 모델을 상세하게 조정하기
7. 솔루션 제시
8. 시스템 론칭 / 모니터링 / 유지 보수
이번 포스팅에서는 데이터를 불러오고, 머신러닝 훈련 바로 전 단계인
훈련용 세트와 테스트 셋을 나누는 과정까지 알아보도록 하겠습니다.
레쓰꽁!!!
1. 데이터 내려받기
import os
import tarfile
import urllib
DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handson-ml/master/"
HOUSING_PATH = os.path.join("datasets", "housing")
HOUSING_URL = DOWNLOAD_ROOT + "datasets/housing/housing.tgz"
def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):
if not os.path.isdir(housing_path):
os.makedirs(housing_path)
tgz_path = os.path.join(housing_path, "housing.tgz")
urllib.request.urlretrieve(housing_url, tgz_path)
housing_tgz = tarfile.open(tgz_path)
housing_tgz.extractall(path=housing_path)
housing_tgz.close()
fetch_housing_data()를 호출하면 현재 작업공간에 datasets/housing 디렉터리를 만들고 housing.tgz 파일을 내려받고 같은 디렉터리에 압축을 풀어 housing.csv파일을 만듭니다.
os.path.join | 경로를 병합하여 새 경로 생성 |
if not os.path.isdir(housing_path): os.makedirs(housing_path) |
directory가 존재하지 않을 시, directory 생성하는 코드 (github의 rickiepark님 코드 참조) |
2. 데이터 불러오기
import pandas as pd #pandas 임포트
def load_housing_data(housing_path=HOUSING_PATH):
csv_path = os.path.join(housing_path, "housing.csv")
return pd.read_csv(csv_path) #데이터를 읽어 들이는 함수
2-3. 데이터 구조 훑어보기
2-3-1. head(), info() 메서드
housing = load_housing_data() #위에서 만든 함수로 데이터 읽어 들이기
housing.head() #데이터 프레임의 첫 5행 확인
housing.info() #전체 행 수, 데이터의 타입과 널이 아닌 값 개수 파악
- 각 행은 하나의 구역을 나타냅니다.
- info() 메서드는 데이터에 대한 간략하 널명과 특히 전체 행 수, 각 특성의 데이터 타입과 NULL이 아닌 값의 개수를 확인하는 데 유용합니다.
***분석에 앞서 head() 메서드와 함께 데이터 구조를 이해하는게 가장 중요하므로 반드시 확인해야 하는 부분!
1. 총 20,640개 샘플
2. 머신러닝 프로젝트치고는 상당히 작은 편이지만, 처음 시작하기에는 적당한 크기
3. total_bedrooms 피쳐는 20,433개만 non-null 인 것으로 보아 207개의 구역은 이 feature가 null값
4. ocean_proximity만 object 타입
5. head()로 ocean_proximity 칼럼의 값이 반복되는 것으로 보아 이 feature는 아마 범주형이라고 추측
6. value_counts() 메소드로 확인해보자
housing['ocean_proximity'].value_counts()
- 총 5개의 카테고리로 구성
2-3-2. describe() 메서드
housing.describe() #숫자형 데이터 요약 정보
1. null 값은 제외됩니다.
2. std는 값이 퍼져 있는 정도를 측정하는 표준편차
3. 25%, 50%, 75%는 백분위수(percentile)
4.데이터의 형태를 빠르게 검토하는 다른 방법은 각 숫자형 특성을 히스토그램으로 그려보는 것
2-3-3. 히스토그램 그리기
import matplotlib.pyplot as plt
%matplotlib inline
housing.hist(bins=50, figsize=(20,15))
plt.show()
1. 중간 소득(median income)이 US 달러로 표현되지 않은 것으로 추정
- 상한 15(실제 15,0001), 하한 0.5(실제 0.4999)가 되도록 스케일 조정한 상태, 즉 3은 실제로 30,000달러를 의미
- 머신러닝에서는 전처리된 데이터를 다루는 경우가 흔하고 이것이 문제가 되지는 않지만 데이터가 어떻게 계산된 것인지 반드시 이해하고 확인
2. 중간 주택 연도(housing median age)와 중간 주택 가격(median housing value)역시 최댓값과 최솟값을 한정
* housing median age는 target(레이블)로 사용되기 때문에 심각한 문제가 될 가능성 有
- 가격이 한계값을 넘어가지 않도록 머신러닝 알고리즘이 학습될지도 모릅니다.
- 이것이 문제가 될지 안 될지는 클라이언트 팀(이 시스템의 출력을 사용할 팀)과 함께 검토하는 것이 좋습니다.
- 만약 그 팀에서 $500,000를 넘어가더라도 정확한 예측값이 필요하다고 한다면 우리가 선택할 수 있는 방법은 두 가지 입니다.
a. 한계값 밖의 구역에 대한 정확한 레이블을 구합니다.
b. 훈련 세트에서 이런 구역을 제거합니다. ($500,000가 넘는 값에 대한 예측은 평과 결과가 매우 나쁠 것이 테스트 세트에서도 제거합니다.)
3. feature의 스케일이 서로 많이 다르기때문에 feature scailing 필수(이 부분에 대해서는 뒷부분에서)
4. 많은 히스토그램의 꼬리가 두꺼운 형태
- 가운데에서 왼쪽보다 오른쪽으로 더 멀리 뻗은 모습
- 이런 형태는 일부 머신러닝 알고리즘에서 패턴을 찾기 어렵기 때문에 좀 더 종 모양의 분포가 되도록 변형 시키자
3. 테스트 세트 만들기
어떤 알고리즘을 사용할지 정하기 전에 전체 데이터를 자세히 파악해야겠죠?
만약 테스트 세트를 들여다본다면 테스트 세트에서 겉으로 드러난 어떤 패턴에 속아 특정 머신러닝 모델을 선택하게 될지도 모릅니다. 이 테스트 세트로 일반화 오차를 추정하면 매우 낙관적인 추정이 되며 시스템을 론칭했을 때 기대한 성능이 나오지 않을 것입니다.
이를 데이터 스누핑(data snooping)편향이라고 합니다.
테스트 세트 만들기는 무작위로 어떤 샘플을 선택해서 데이터 셋의 20% 정도(데이터 셋이 매우 크다면 그보다 적게)를 떼어놓으면 됩니다.
import numpy as np
#트레인 셋, 테스트 셋 나누기 위한 함수
def split_train_test(data, test_ration):
shuffled_indices = np.random.permutation(len(data))
test_set_size = int(len(data) * test_ration)
test_indices = shuffled_indices[:test_set_size]
train_indices = shuffled_indices[test_set_size:]
return data.iloc[train_indices], data.iloc[test_indices]
#트레인 셋, 테스트 셋 나누는 함수에 매개변수 주고 함수 적용하기
train_set, test_set = split_train_test(housing, 0.2)
#트레인 셋, 테스트 셋 크기 확인
len(train_set) #16512
len(test_set) #4128
- train_set 16,512개 / test_set 4,128개 확인!
- 프로그램 재실행시, 다른 테스트 세트가 생성됩니다. (np.random.permutation으로 인해)
- 여러 번 실행시키면 전체 데이터셋을 보는 셈이므로 이를 피하기 위한 방법을 찾아야 합니다.
(생략...)
사이킷런에은 데이터셋을 여러 서브셋으로 나누는 다양한 방법을 제공합니다.
from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(housing, test_size = 0.2, random_state = 42)
- 사이킷런의 train_test_split은 앞서 만든 split_train_set와 아주 비슷하지만 두 가지 특징이 더 있습니다.
1. 난수 초기값을 지정할 수 있는 random_state 매개변수 존재
2. 행의 개수가 같은 여러 개의 데이터셋을 넘겨서 같은 인덱스를 기반으로 나눌 수 있다.
(이 내용은 이해가 잘 안되서 내부동작을 한번더 살펴봐야 할 것 같다....)
- 데이터셋이 충분히 크다면 이처럼 무작위 샘플링 방식 OK.
하지만 그렇지 않다면 샘플링 편향이 생길 가능성 大
예를 들어, 전체 인구를 대표할 수 있는 1,000명을 선택하기 위해서는 인구의 비율을 유지하면서 샘플링 해야합니다.
인구 비율이 여:남 = 51.3%: 48.7% 라면 여성은 513명, 남성은 487명을 대상으로 설문을 진행해야 합니다.
=> 이를 계층적 샘플링(stratified samplig)이라고 합니다.
전체 인구는 계층(strata)라는 동질의 그룹으로 나뉘고, 테스트 세트가 전체 인구를 대표하도록 각 계층에서 올바른 수의 샘플을 추출합니다.
- 전문가가 중간 소득이 중간 주택 가격을 예측하는 데 매우 중요하다고 이야기해주었다고 가정합시다!
이 경우, 테스트 세트가 전체 데이터셋에 있는 여러 소득 카테고리를 잘 대표해야합니다.
중간 소득이 연속형 특성을 가진 숫자이므로 카테고리 feature를 만들어야 합니다.
위에서 그려낸 중간 소득의 히스토그램을 조금 더 자세히 살펴봅시다.
- 중간 소득 대부분은 1.5~6 ($15,000 ~ $60,000) 사이에 분포해 있지만 일부는 $60,000를 넘기도 합니다.
- 계층별로 데이터셋에 충분한 샘플 수가 있어야 합니다. (정규분포를 따라야함)
- 그렇지 않으면 계층의 중요도를 추정하는데 편향이 발생할 것입니다.
- 즉, 너무 많은 계층으로 나누면 안 된다는 뜻이고 각 계층이 충분히 커야 한다는 것입니다.
#pandas의 cut 함수를 사용해 카테고리화한 feature 생성
housing["incom_cat"] = pd.cut(housing["median_income"],
bins = [0., 1.5, 3.0, 4.5, 6., np.inf],
labels = [1,2,3,4,5])
- 판다스의 cut() 함수를 사용해 카테고리 5개를 가진 소득 카테고리 feature 생성
- 1~5까지 레이블을 가짐
- 카테고리 1은 0에서 1.5까지의 범위 (즉 $15,000 이하) 카테고리 2는 1.5에서 3사이가 되는 식입니다.
#카테고리화 한 median_income 히스토그램 그리기
housing["incom_cat"].hist()
plt.show()
히스토그램으로 확인해봅니다.
이제 소득 카테고리를 기반으로 계층 샘플링 가능!
#사이킷런의 StratifiedShuffleSplit 사용해 계층 샘플링
from sklearn.model_selection import StratifiedShuffleSplit
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_index, test_index in split.split(housing, housing["incom_cat"]):
strat_train_set = housing.loc[train_index]
strat_test_set = housing.loc[test_index]
그럼, 테스트 세트에서 소득 카테고리의 비율 살펴봅시다.
strat_test_set['incom_cat'].value_counts() / len(strat_test_set)
즉, 이 책에서는 일반 무작위 샘플링보다는 계층 샘플링을 사용해 만든 테스트 세트가 전체 데이터셋에 있는 소득 카테고리의 비율과 거의 같음을 설명하고 있습니다.
이제 income_cat 칼럼을 삭제해서 데이터를 원래 상태로 되돌립시다!
for set_ in (strat_train_set, strat_test_set):
set_.drop("incom_cat", axis=1, inplace=True)
테스트 세트에 대해서 아주 자세히 설명해주고 있는데요.
이는 종종 등한시되기도 하지만 머신러닝 프로젝트에서 아주 중요한 부분입니다.
이런 아이디어들은 나중에 교차 검증에 대해 이야기할 때 도움이 됩니다.
다음 포스팅에서는 본격적으로 데이터 탐색을 해보겠습니다!
참고자료
-
'Python > ML' 카테고리의 다른 글
알고리즘 이해하기 - 유클리드 거리 (0) | 2021.03.12 |
---|---|
[머신러닝] 캘리포니아 주택 가격 예측 - 1. 큰 그림 보기 (0) | 2020.10.16 |