Machine Learning/Advanced (hands on machine learning)

3- 머신러닝 프로젝트 - 데이터 가져오기

jwjwvison 2021. 5. 15. 13:03
import os
import tarfile
import urllib.request

DOWNLOAD_ROOT = "https://raw.githubusercontent.com/rickiepark/handson-ml2/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()

 fetch_housing_data() 를 호출하면 현재 작업 공간에 datasets/housing 디렉터리를 만들고 housing.tgz파일을 내려받고 같은 디렉터리에 압축을 풀어 housing.csv 파일을 만든다.

import pandas as pd

def load_housing_data(housing_path=HOUSING_PATH):
    csv_path = os.path.join(housing_path, "housing.csv")
    return pd.read_csv(csv_path)

 이 함수는 모든 데이터를 담은 판다스의 데이터프레임 객체를 반환한다.

 

 DataFrame의 head() 메서드를 사용해 처음 다섯 행을 확인해보자.

housing = load_housing_data()
housing.head()

 

 info() 메서드는 데이터에 대한 간략한 설명과 특히 전체 행 수, 각 특성의 데이터 타입과 null이 아닌 값의 개수를 확인하는데 유용하다.

housing.info()

ocean_proximity 필드만 빼고 모든 특성이 숫자형이다.

 어떤 카테고리가 있고 각 카테고리마다 얼마나 많은 구역이 있는지 value_counts() 메서드로 확인한다.

housing['ocean_proximity'].value_counts()

 

 describe() 메서드는 숫자형 특성의 요약 정보를 보여준다

housing.describe()

 

 데이터의 형태를 빠르게 검토하는 다른 방법은 각 숫자형 특성을 히스토그램으로 그려보는 것이다. 특성마다 따로 히스토그램을 그릴 수도 있고 전체 데이터셋에 대해 hist() 메서드를 호출하면 모든 숫자형 특성에 대한 히스토그램을 출력한다.

%matplotlib inline
import matplotlib.pyplot as plt
housing.hist(bins=50,figsize=(20,15))
plt.show()

 

 테스트 세트로 일반화 오차를 추정하면 매우 낙관적인 추정이 되며 시스템을 론칭했을 때 기대한 성능이 나오지 않을 것이다. 이를 데이터 스누핑(data snooping) 편향이라고 한다.

 사이킷런은 데이터셋을 여러 서브셋으로 나누는 다양한 방법을 제공한다. train_test_split 으로 행의 개수가 같은 여러 개의 데이터셋을 넘겨서 같은 인덱스를 기반으로 나눌 수 있다. ( 이는 예를 들어 데이터프레임이 레이블에 따라 여러 개로 나뉘어 있을 때 매우 유용하다)

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() 함수는 파이썬 리스트, 넘파이 배열, 판다스 데이터프레임과 판다스 시리즈 객체 등을 입력으로 받을 수 있다.

 

지금까지는 순수한 무작위 샘플링 방식을 보았는데 데이터셋이 특성 수에 비해 충분히 크지 않다면 샘플링 편향이 생길 가능성이 크다. 예를들어 설문조사 남 여 비율이 4:6 이라면 무작위로 뽑은 표본에서도 남 여 비율이 4:6을 유지해야 한다. 이를 계층적 샘플링(stratified sampling) 이라고 한다.

 

 위 데이터에서 중간 소득이 중간 주택 가격을 예측하는 데 매우 중요하다고 이야기해주었다고 가정하자. 이 경우 테스트 세트가 전체 데이터셋에 있는 여러 소득 카테고리를 잘 대표해야 한다. 중간 소득이 연속적인 숫자형 특성이므로 소득에 대한 카테고리 특성을 만들어야 한다.

housing['income_cat']=pd.cut(housing['median_income'],bins=[0.,1.5,3.0,4.5,6.,np.inf],
                            labels=[1,2,3,4,5])
housing['income_cat'].hist()

 이제 소득 카테고리를 기반으로 계층 샘플링을 할 준비가 되었다. 사이킷런의 Stratified ShuffleSplit를 사용할 수 있다.

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['income_cat']):
  strat_train_set=housing.loc[train_index]
  start_test_set=housing.loc[test_index]
strat_train_set['income_cat'].value_counts() / len(start_test_set)

 

 실제로는 다음과 같은 코드로 계층적으로 테스트셋을 나눈다.

st_train_set,st_test_set= train_test_split(housing,test_size=0.2,random_state=42,stratify=housing['income_cat])

 

 이제 income_cat 특성을 삭제해서 데이터를 원래 상태로 되돌리겠다.

for set_ in (strat_train_set,start_test_set):
  set_.drop('income_cat',axis=1,inplace=True)