1. 데이터
- 주어진 데이터 : Sample + label -> 지도 학습
- 주택 가격이라는 특정 값 예측 -> 회귀 문제
- 예측에 사용할 특성 다수(인구 수, 중간 소득 등) -> 다중 회귀
- 각 구역마다 하나의 값 예측(주택 가격 하나만을 예측) -> 단변량 회귀 <==> 구역마다 여러 값을 예측한다면 다변량 회귀
- 데이터가 상대적으로 작음 -> 일반적인 배치 학습 <==> 데이터 매우 크면 맵리듀스 기술 활용
2. 성능 측정 지표 선택
- 회귀 문제의 전형적인 성능 지표 -> 평균 제곱근 오차 (root mean square error, RMSE)
- RMSE -> 오차가 커질수록 RMSE가 더욱 커지므로 예측에 대한 오류의 크기를 확인할 수 있다.
- RMSE(X,h) -> 가설 h를 사용하여 일련의 샘플을 평가하는 비용함수 (loss function)
- 이상치로 보이는 구간이 많은 경우 -> 평균 절대 오차 (MAE) 고려
3. 데이터 확인
import os
import tarfile
import urllib
DOWNLOAD_ROOT = "다운받고자 하는 파일의 URL주소/"
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):
os.makedirs(housing_path, exist_ok = True)
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()
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)
housing = load_housing_data()
housing.head()
- info(), describe() 등을 활용하여 데이터 확인
housing.info()
- object 형인 ocean_proximity에 어떤 종류의 객체가 존재하는지 확인
housing["ocean_proximity"].value_counts()
- 히스토그램을 활용하여 데이터 확인 ( describe() 함수의 직관화)
import matplotlib.pyplot as plt
%matplotlib inline
housing.hist(bins=50, figsize(20,15))
plt.show()
hist()를 통해 알 수 있는 점
1. 중간 소득(median income) 의 x축이 2,4,6,8,10 ... 의 형식 --> 2는 2만달러를 의미
2. 중간 주택 연도(housing median age) & 중간 주택 가격(median house value) 최댓값 한정시킴 --> 해당 구역 제거 or 구역 이외의 데이터도 수집
3. 특성들의 스케일이 다름
4. 대부분의 히스토그램의 꼬리가 두꺼움 --> 패턴 찾기 어려우므로 정규화를 통해 종 모양으로 변경할 필요 있음
- 테스트 세트 만들기
1. 테스트 세트를 활용하게 되면 "데이터 스누핑"으로 인해 낮은 성능 발생
2. 과대적합
3. 위의 문제를 해결하기 위해 테스트 세트는 절대 들여다보지 않는다.
방법 1
import numpy as np
def split_train_test(data, test_ratio):
shuffled_indices = np.random.permutation(len(data))
test_set_size = int(len(data) * test_ratio)
test_indicies = shuffled_indices[:test_set_size]
train_indicies = shuffled_indices[test_set_size:]
return data.iloc[train_indicies], data.iloc[test_indicies]
train_split, test_split = split_train_test(housing, 0.2)
데이터를 무작위 셔플 후 8:2 비율로 split
--> 문제점 : 코드 실행마다 무작위 셔플되므로 결국 테스트 데이터를 모두 훈련하게 되어 스누핑 편향 발생
해결 : 방법 2
np.random.seed(42)
np.random.permutation()
...
랜덤 시드 (시드 넘버는 사용자 마음) 적용, --> 항상 같은 난수 인덱스가 생성, (시드 넘버 42에 해당되는 난수에 의해 데이터 분리)
--> 문제점 : 다음번에 업데이트된 데이터셋을 사용하려면 문제가 됨
해결 : 방법 3
샘플의 식별자 사용 (고유 식별자, 변경 불가능한 식별자가 존재한다고 가정)
from zlib import crc32
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]
housing_with_id = housing.reset_index()
#인덱스를 특성으로 추가, (인덱스 열이 하나 생김)
train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "index")
#새로 생성된 index 열에 고유 식별자를 추가하는 방식
해결 : 방법 4
사이킷런의 함수 활용 (방법 3과 매우 유사한 방법)
from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
(+ 위의 방법들은 순수한 무작위 샘플링 방식이다. 만약 데이터셋이 충분히 크지 않다면 샘플링 편향이 발생할 수 있다. 예를 들어 10, 20, 30대 별로 설문 조사를 하기 위해서는 전화번호부에서 무작위로 1000개의 샘플을 뽑지 말아야한다. 10대에서 몇 명, 20대에서 몇 명, 30대에서 몇 명... 이렇게 계층적 샘플링이 이뤄져야한다. 뿐만 아니라 각 샘플들은 다른 샘플들을 잘 '대표'해야 한다.)
+ 계층적인 샘플링을 수행할 경우 (소득 카테고리 기반으로 각 계층을 cut함)
housing["income_cut"] = pd.cut(housing["median_income"], bins=[0.,1.5,3.0,4.5,6., np.inf], labels=[1,2,3,4,5])
housing["income_cut"].hist()
--> 각 카테고리에는 충분한 샘플의 수가 존재해야하므로 너무 세세하게 나누면 안된다.
위에서 만든 housing["income_cut"] 을 활용, 계층 샘플링을 한다.
from sklearn.model_selection import StratifiedShuffleSplit
#k-fold 계층 샘플링 + shuffledSplit 랜덤 샘플링을 합친 것
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_index, test_index in split.split(housing, housing["income_cut"]):
strat_train_set = housing.loc[train_index]
strat_test_set = housing.loc[test_index]
(income_cut으로 계층을 나눈 후 --> 각 계층 별 train, test 샘플링 실행)
4. 시각화 & 데이터 탐색
- 산점도를 활용한 데이터 시각화
housing = strat_train_set.copy()
housing.plot(kind="scatter", x="longitude", y="latitude")
housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.1)
#밀집도 확인 가능
(+ 특정 패턴을 얻기 위해 alpha 옵션 사용)
(+산점도의 활용 : 원의 반지름과 색상을 활용하여 구역의 인구 & 가격 시각화)
housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4, s=housing["population"]/100,
label="poplulation", figsize=(10,7),
c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True,)
#s = 원의 반지름, 인구
#c = 색상, 가격
#jet = 미리 정의된 컬러 맵, 낮은 가격(파랑)~높은 가격(빨강)
plt.legend()
(+ 바다와 가까운 곳 --> 인구 밀집 --> 군집성을 띄고 있음!!)
- 상관관계 조사 (주택 가격 기준)
corr_matrix = housing.corr()
corr_matrix["median_house_value"].sort_values(ascending=False)
(+ -1에 가까우면 강한 음의 상관관계, +1에 가까우면 강한 양의 상관관계, 상관계수는 선형적인 상관관계만 측정하므로 비선형적인 관계는 확인할 수 없다.)
- 산점도를 활용하여 상관관계 확인 (특성이 많으므로 몇 개의 특성만 확인함)
from pandas.plotting import scatter_matrix
attributes = ["median_house_value", "median_income", "total_rooms", "housing_median_age"]
scatter_matrix(housing[attributes], figsize=(12,8))
(+ 각 변수들의 상관관계를 직관적으로 확인할 수 있다. ex. median_income와 median_house_value 은 선형적인 관계를 띄고 있다.)
- 변수를 추가하여 상관관계를 파악하는 방법
housing["rooms_per_household"] = housing["total_rooms"] / housing["households"]
housing["bedrooms_per_room"] = housing["total_bedrooms"] / housing["total_rooms"]
housing["population_per_household"] = housing["population"] / housing["households"]
corr_matrix = housing.corr()
corr_matrix["median_house_value"].sort_values(ascending=False)
4. 머신러닝 알고리즘을 위한 데이터 준비
데이터 준비 자동화의 필요성
- 어떤 데이터셋에 대해서도 데이터 변환이 쉬움
- 향후 프로젝트에 사용할 수 있는 변환 라이브러리 점진적으로 구축
- 실제 시스템에서도 사용 가능
- 여러 가지 데이터 변환을 쉽게 시도할 수 있음 --> 최적의 조합 발견이 용이함
housing = strat_train_set.drop("median_house_value", axis=1)
housing_labels = strat_train_set["median_house_value"].copy()
#훈련 데이터와 레이블 데이터 분리
dropna(), fillna(), drop() 를 활용한 이상치, 결측치 제거
housing.dropna(subset=["total_bedrooms"]) #옵션 1
housing.drop("total_bedrooms", axis=1) #옵션 2
median = housing["total_bedrooms"].median() #옵션 3
housing["total_bedrooms"].fillna(median, inplace=True)
사이킷런의 SimpleImputer을 활용한 누락치 제거
위의 옵션 3을 쉽게 구현 가능.
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="median")
중간값이 수치형 특성에서만 계산 가능하므로 object형인 ocean_proximity를 제외한 복사본 생성
housing_num = housing.drop("ocean_proximity", axis=1)
imputer.fit(housing_num)
imputer는 각 특성의 중간 값을 계산해서 그 결과를 객체의 statistics_ 속성에 저장함
imputer.statistics_
학습된 imputer 객체 (중간값이 저장된) 를 사용해 훈련 세트에서 누락된 값을 학습한 중간값으로 변경
X = imputer.transform(housing_num)
housing_tr = pd.DataFrame(X, columns = housing_num.columns, index = housing_num.index)
housing_tr
텍스트와 범주형 데이터 핸들링
housing["ocean_proximity"] 는 object형 데이터
housing_cat = housing[["ocean_proximity"]]
housing_cat.head(10)
텍스트를 "벡터"의 형태로 변경하여 수치형 취급한다.
방법 1 : (5개의 범주를 각각 0,1,2,3,4 의 숫자형태로 변환)
from sklearn.preprocessing import OrdinalEncoder
ordinal_encoder = OrdinalEncoder()
housing_cat_encoded = ordinal_encoder.fit_transform(housing_cat)
housing_cat_encoded[:10]
--> 문제점 : 이 방법은 0 : 매우 안 좋음, 1,2,3, 4 : 매우 좋음 과도 같은 순서형 데이터에서 사용된다. 즉, 해당 예제는 명목형 데이터이므로 이러한 방법은 사용하면 안된다. (위의 방법은 벡터 간의 거리를 중요하게 생각하기 때문이다.)
방법 2 : (원-핫 인코딩, 더미변수 생성)
from sklearn.preprocessing import OneHotEncoder
onehot = OneHotEncoder()
housing_cat_onehot = onehot.fit_transform(housing_cat)
housing_cat_onehot.toarray()
--> 각 카테고리를 임베딩이라 부르는 학습 가능한 저차원 벡터로 만든다.
사용자 정의 변환기 제작
- 사이킷 런 : 덕 타이핑 지원 (객체의 속성 or 메서드가 객체의 유형을 결정)
- fit(), transform(), fit_transform() 등 사용자 정의 변환기 제작이 가능함
- 파이썬에서 이름에 Maxin 이 존재하면 객체의 기능을 확장하려는 목적으로 만들어진 클래스를 나타낸다. 예를 들어 TransformerMaxin 은 fit_transform() 메서드를 상속받아 해당 기능을 수행할 수 있으며 TransformMaxin을 상속받는 모든 파이썬 클래스에 fit_transform()을 제공한다.
- BaseEstimator를 상속하면 (동시에 생성자에 *args나 **kwargs를 사용하지 않으면) 하이퍼파라미터 튜닝에 필요한 두 메서드 (get_params()와 set_params()) 를 추가로 얻을 수 있다.
(+참고, *args : 어떤 값을 넣을지는 모르겠는데 값을 넣으면 함수에 변수가 튜플 형태로 입력된다.
**kwargs : 어떤 값을 넣을지는 모르겠는데 값을 넣으면 변수가 딕셔너리 형태로 입력된다.)
--다음 포스팅에 계속--
'핸즈온머신러닝&딥러닝' 카테고리의 다른 글
MNIST 활용, 분류 (0) | 2021.05.18 |
---|---|
캘리포니아 주택 가격 예측2 (0) | 2021.05.16 |
RNN과 어텐션을 사용한 자연어 처리 2 (0) | 2021.05.05 |
RNN과 어텐션을 사용한 자연어 처리 1 (0) | 2021.05.02 |
RNN과 CNN을 사용해 시퀀스 처리하기 2 (0) | 2021.04.30 |