데이터: 핸즈온 머신러닝(2판)의 예제 데이터(https://github.com/rickiepark/handson-ml2/blob/master/datasets/housing/housing.csv) 사용

 

 

1. 문제 정의

예제 데이터는 캘리포니아의 블록 그룹마다 인구, 중위소득, 중간주택가격 등의 변수를 담은 데이터

※블록 그룹: 미국 인구조사국이 샘플 데이터 발표에 사용하는 최소한의 지리적 단위, 보통 600명~3,000명의 인구

 

● 첫번째 질문: 비즈니스의 목적이 무엇인가?

 -모델을 이용해 어떻게 이익을 얻으려는 건지?

 -문제를 어떻게 구성할지, 어떤 알고리즘을 선택할지, 어떤 성능 지표로 모델을 평가할지, 모델 튜닝을 어느 정도로 빡세게 할지 경정하는데 중요

 

ex 답변) 다른 블록 그룹의 데이터가 주어졌을 때 중간 주택 가격을 예측하여 해당 지역에 투자할 가치가 있는지 평가하는 것이 문제

 

 

● 두번째 질문: 현재 솔루션은 어떻게 구성되어 있는가?

 -문제 해결 방법에 대한 정보 또는 참고 성능으로 활용 가능

 

ex 답변) 한 팀이 블록 그룹의 최신 정보를 모으고 전문가들이 복잡한 규칙을 통해 수동으로 예측 중

→인구조사 데이터는 인구 관련 데이터에 더해 블록 그룹의 중간 주택 가격 데이터를 포함하므로 매우 적합한 데이터셋으로 보임

 

 

● 세번째 질문: 지도/비지도/강화 학습 중 무엇?

                        분류/회귀 중 무엇?

                        배치 학습/온라인 학습 중 무엇?

→중간 주택 가격이 나와있는 훈련 샘플이 있으므로 지도 학습 작업

→중간 주택 가격이라는 값을 예측해야하므로 회귀 문제

→예측에 사용할 특성이 인구, 중간 소득 등 한 개 이상이므로 다중 회귀

→중간 주택 가격 한 개의 값만 예측하므로 단변량 회귀(두 개이상 예측 시 다변량 회귀)

→데이터에 연속적 흐름 없이 고정된 데이터이고 크기도 작으므로 배치 학습이 적정

 

 

 성능 평가 지표 선택

 -회귀 성능 평가 지표: MAE / MSE / RMSE / MSLE / RMSLE / R²

 -분류 성능 평가 지표: 정확도 / 오차행렬 / 정밀도 / 민감도(재현율) / 특이도 / F1 Score / ROC / AUC

 

 

위에서 설정한 가정들 검사해보기

 

 

2. 데이터 가져오기

데이터를 다운로드한 뒤 파이썬 개발환경에 불러오기

import pandas as pd

housing=pd.read_csv('housing.csv',encoding='cp949')

 

데이터 구조 훑어보기

# 불러온 데이터의 처음 다섯 행 확인(괄호안에 숫자 지정하여 더 많은 데이터 확인 가능)
housing.head()

# 불러온 데이터의 마지막 다섯 행 확인(괄호안에 숫자 지정하여 더 많은 데이터 확인 가능
housing.tail()

# 데이터의 간략한 설명, 전체 행 수, 각 변수의 데이터 타입, 결측값(NULL) 개수 확인
housing.info()

# 범주형 변수의 확인
housing['ocean_proximity'].value_count()
# 각 범주별 개수 확인 가능

# 숫자형 변수의 특성(개수, 평균, 표준편차, 최소값, 최대값, 사분위수) 요약
housing.describe()
# 시각적으로 데이터의 분포 알아보기
import matplotlib.pyplot as plt
housing.hist(bins=50,figsize=(20,15))
plt.show()
# 각 변수의 히스토그램을 통해 분포 확인 가능

 

 

데이터 탐색과 시각화(EDA, 탐색적 자료분석)

 -지리적 데이터 시각화: 데이터에 위도(latitude)와 경도(longitude)가 있으므로 이를 이용해 산점도에 위치 데이터 시각화

# plot의 종류는 산점도로, x축에 경도 값, y축에 위도 값 설정, alpha=0.1로 설정하여 투명도 낮추어 밀집된 곳 파악
housing.plot(kind='scatter', x='longitude', y='latitude', alpha=0.1)

 -plot() 함수의 각종 매개변수를 설정해 다양한 값을 한번에 시각화

housing.plot(kind='scatter', x='longitude', y='latitude', alpha=0.4,
                   s=housing['population'],label='population',figsize=(10,7),
                   c='median_house_value', cmap=plt.get_cmap('jet'), colorbar=True,
                   sharex=False)
plt.legend()

 -주택가격은 바다와 밀접한 곳, 인구 밀도 등과 관련이 크다는 점 발견

 

 

 상관관계 조사

 -상관계수를 통해 확인

# housing 데이터의 상관계수를 median_house_value 변수에 대한 상관계수만 내림차순으로 출력
housing.corr()['median_house_value'].sort_values(ascending=False)

 

 -산점도를 통해 확인

# 숫자형 변수 사이의 산점도를 그려주는 판다스 함수
from pandas.plotting import scatter_matrix

# 상관계수 확인 결과 median_house_value와 상관관계가 높아보이는 3개의 변수만 확인
attributes=['median_house_value', 'median_income', 'total_rooms', 'housing_median_age']
scatter_matrix(housing[attributes], figsize=(12,8))

 

 -확인 결과  median_house_value와 median_income이 특히 선형적인 관계가 강한 것을 확인

housing.plot(kind='scatter', x='median_income', y='median_house_value', alpha=0.1)

 -위 그래프에서도 median_house_value가 500000, 450000, 350000일 때 수평선이 확인되며 알고리즘이 이 수평선을 학습하지 않도록 제거해야할 필요가 있음

 

 

-이외에도 특성의 분포에 따라 로그 스케일, Min-Max 스케일 등 변환 과정을 거쳐야 함

 

 특성 조합

 -분포를 정규화하고 상관관계를 확인한 뒤 마지막으로 해볼 수 있는 것은 특성 조합하여 새로운 특성(파생변수) 만들기

 -예시에서 '방 개수'보다는 '가구당 방 개수'가 더 유용할 것이므로 '가구 수'와 '방 개수' 변수를 조합해 '가구당 방 개수' 변수 만들기

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']

 -새로 만든 bedrooms_per_room 변수가 전체 방 개수나 침실 개수보다 중간 주택 가격과 더 큰 상관관계를 가짐

 

 -여러 파생변수들을 만들고 결과를 확인하는 반복적인 과정을 통해 좋은 모델 만들 수 있음

 

 

 

3. 데이터 전처리

housing 데이터에서 예측해야하는 median_house_value 변수는 분리하기

housing_labels=housing['median_house_value']

 

수치형 변수 결측값 처리

 -해당 구역을 제거(dropna())

 -전체 특성 제거(drop())

 -어떤 값(0, 평균, 중간값 등)으로 채움(fillna(), SimpleImputer)

 -SimpleImputer 사용예시

from sklearn.impute import SimpleImputer

# SimpleImputer 객체 생성, 중간값을 채우는 것으로 설정
imputer=SimpleImputer(strategy='median')

# 숫치형 변수에 대해 중간값으로 결측값을 채울 것이므로 범주형 변수인 ocean_proximity와 예측할 변수인 median_house_value는 제거
housing_num=housing.drop(['ocean_proximity','median_house_value'],axis=1)

imputer.fit(housing_num)

# statistics_에 각 변수별로 채울 값을 저장(여기서는 중간값)
imputer.statistics_

# housing_num의 숫치형 변수를 피팅한 imputer를 housing_num에 적용
X=imputer.transform(housing_num)

# 결측값이 중간값으로 대체된 데이터에 원래의 변수명과 인덱스를 적용해 원래 데이터 형태로 만들기
housing_tr=pd.DataFrame(X,columns=housing_num.columns,index=housing_num.index)

 

 

 

 범주형 변수 인코딩

 -범주형 변수인 ocean_proximity의 특성 살펴보기

# 범주형 변수인 ocean_proximity만 housing_cat에 저장하고 확인
housing_cat=housing[['ocean_proximity']]
housing_cat

 -OrdinalEncoder, OneHotEncoder, LabelEncoder 등의 인코더를 사용하여 범주형 변수를 수치형 변수로 변환

 -OrdinalEncoder 사용예시

from sklearn.preprocessing import OrdinalEncoder

# ordinalencoder 객체 생성
ordinal_encoder=OrdinalEncoder()

# 범주형 변수인 housing_cat을 ordinalencoder에 피팅하고 변환까지 적용
housing_cat_encoded=ordinal_encoder.fit_transform(housing_cat)
housing_cat_encoded

# 인코딩에 사용된 범주 확인
ordinal_encoder.categories_

 

 

 

 -OneHotEncoder 사용예시

from sklearn.preprocessing import OneHotEncoder

# OneHotEncoder 객체 생성
cat_encoder=OneHotEncoder()

# 범주형 변수인 housing_cat을 인코더에 피팅하고 변환
housing_cat_1hot=cat_encoder.fit_transform(housing_cat)
housing_cat_1hot

# Sparse Row(희소행렬) 형태로 출력되므로 toarray()를 사용하여 넘파이 배열의 형태로 출력할 수 있음
housing_cat_1hot.toarray()

# 인코딩에 사용된 범수 확인
cat_encoder.categories_

 

 

 특성 스케일링

 -모든 특성의 범위를 같게 해줌(0~1 사이 또는 -1~1 사이 등)

 -Min-Max 스케일링, 표준화(StandardScalling) 등

 

 

 변환 파이프라인

 -앞에서 했던 변환을 순서대로 처리할 수 있도록 파이프라인 생성 가능

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
num_pipeline=Pipeline([('imputer',SimpleImputer(strategy='median')),    # SimpleInputer를 사용해 결측값을 중간값으로 설정
                                   ('attribs_adder',CombinedAttributesAdder()),      # 변수 조합 생성
                                   ('atd_scaler', StandardScaler())                         # 정규화를 통해 범위 조정
                                  ])

housing_num_tr=num_pipeline.fit_transform(housing_num)

 

 -ColumnTransformer를 사용하면 파이프라인 하나에 범주형과 수치형을 동시에 넣어 열마다 처리할 수 있음

from sklearn.compose import ColumnTransformer
num_attribs=list(housing_num)	# 수치형 변수열
cat_attribs=['ocean_proximity']	# 범주형 변수열

full_pipeline=ColumnTransformer([('num',num_pipeline,num_attribs),          # 수치형 변수열에는 앞에서 생성한 수치형 변수 변환 파이프라인 적용
                                                  ('cat',OneHotEncoder(),cat_attribs)])       # 범주형 변수열에는 OneHotEncoder 적용하여 수치형 변수로 변환

housing_prepared=full_pipeline.fit_transform(housing)

 

 

 

 

4. 모델 선택과 훈련

 훈련 세트에서 훈련, 평가

 -전처리를 끝낸 데이터를 적절한 알고리즘을 선택해서 훈련시키기

# 선형 회귀 모델
from sklearn.linear_model import LinearRegression
# 선형 회귀 모델 객체 생성
lin_reg=LinearRegression()
# 데이터를 선형 회귀 모델에 훈련시키기
lin_reg.fit(housing_prepared,housing_labels)


# 의사결정나무 모델
from sklearn.tree import DecisionTreeRegressor
# 의사결정나무 모델 객체 생성
tree_reg=DecisionTreeRegressor()
# 데이터를 의사결정나무 모델에 훈련시키기
tree_reg.fit(housing_prepared, housing_labels)

 -훈련된 모델의 성능 평가

from sklearn.metrics import mean_squared_error

# 선형회귀모델의 성능 평가
# 훈련된 모델에 데이터를 넣었을 때 예측값 계산
housing_predictions=lin_reg.predict(housing_prepared)

# 평균제곱오차(mse) 계산
lin_mse=mean_squared_error(housing_labels,housing_predictions)

# mse에 루트를 씌운 값은 rmse 계산
lin_rmse=np.sqrt(lin_mse)

# rmse값
lin_rmse

### 결과 ###
1.6715935001871568e-10
from sklearn.metrics import mean_squared_error

# 의사결정나무 모델의 성능 평가
# 훈련된 모델에 데이터를 넣었을 때 예측값 계산
housing_predictions=tree_reg.predict(housing_prepared)

# 평균제곱오차(mse) 계산
tree_mse=mean_squared_error(housing_labels,housing_predictions)

# mse에 루트를 씌운 값은 rmse 계산
tree_rmse=np.sqrt(tree_mse)

# rmse값
tree_rmse

### 결과 ###
0.0

 -성능 평가 지표의 값이 거의 0에 가깝거나 0이 나온 것은 훈련 데이터에 모델이 과대적합(오버피팅) 되었기 때문

 

 

 교차 검증을 통한 평가

 -훈련 데이터를 정해진 개수(CV)만큼의 서브세트로 무작위로 분할하고 1개의 서브세트를 평가에 활용하고 나머지 서브세트를 훈련에 활용하는 방법

from sklearn.model_selection import cross_val_score

# 의사결정나무 모델에서 교차검증을 통해 mse계산
# 사이킷런의 교차검증 기능은 scoring 매개변수에 낮을수록 좋은 비용함수가 아닌 높을수록 좋은 효용함수를 기대하므로
# neg_mean_squared_error를 계산하여 낮을수록 좋은 점수로 만듦
scores=cross_val_score(tree_reg,housing_prepared,housing_labels,scoring='neg_mean_squared_error',cv=10)
tree_rmse_scores=np.sqrt(-scores)


# 서브세트 개수만큼 계산된 rmse의 평균과 표준편차를 계산
def display_scores(scores):
    print('점수:',scores)
    print('평균:',scores.mean())
    print('표준편차:',scores.std())

display_scores(tree_rmse_scores)

# 선형회귀 모델에도 적용하여 선형회귀 모델을 교차검증하였을 때 점수 계산
lin_scores=cross_val_score(lin_reg,housing_prepared,housing_labels,scoring='neg_mean_squared_error',cv=10)
lin_rmse_scores=np.sqrt(-lin_scores)
display_scores(lin_rmse_scores)

 -선형회귀 모델이 의사결정나무 모델보다 과대적합이 덜하여 교차 검증을 했을 때도 더 좋은 점수를 가짐

 

 

 

 

5. 모델 세부 튜닝

 -모델에 사용되는 하이퍼 파라미터 등을 조율하며 가장 좋은 점수를 도출하는 하이퍼 파라미터를 찾아 모델에 적용시키기

 

 그리드 탐색: 탐색하고자 하는 하이퍼 파라미터를 전부 지정하면 모든 하이퍼 파라미터 조합에 대해 교차 검증을 사용해 평가

 -랜덤 포레스트 사용 예시

from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import GridSearchCV

# 그리드 서치에 적용하여 탐색할 파라미터들 지정
param_grid=[{'n_estimators':[3,10,30],'max_features':[2,4,6,8]},
            {'bootstrap':[False],'n_estimators':[3,10],'max_features':[2,3,4]}]
# 첫번째 딕셔너리에서 n_estimators가 각각 3, 10 ,30일 때와 max_features가 각각 2, 4, 6, 8일 때의 조합으로 총 12번 평가
# 두번째 딕셔너리에서는 총 6번 평가하며 bootstrap은 False로 설정
# 총 12+6=18개의 조합을 탐색하고 CV=5로 교차검증을 5번 시도하여 총 훈련횟수는 18*5=90번


# 랜덤포레스트 객체 생성
forest_reg=RandomForestRegressor()

# 랜텀포레스트 모델과 파라미터, 교차검증 횟수, 평가 지표 등을 매개변수로 지정한 그리드 서치 모델 객체 생성
grid_search=GridSearchCV(forest_reg,param_grid,cv=5,scoring='neg_mean_squared_error',return_train_score=True)

# 데이터를 그리드 서치 모델에 피팅하여 최적의 하이퍼 파라미터 도출
grid_search.fit(housing_prepared,housing_labels)

# 최적의 하이퍼 파라미터 출력
gird_search.best_params_

### 결과 ###
{'max_features': 8, 'n_estimators': 30}

 

랜덤 탐색: 지정한 하이퍼 파라미터를 랜덤으로 조합하여 교차 검증을 사용해 평가

 

 

 중요도 탐색: 모델이 정확한 예측을 하기 위한 각 변수의 상대적인 중요도

# 각 변수의 상대적인 중요도 계산
feature_importances=grid_search.best_estimator_.feature_importances_

# 계산된 중요도와 변수의 이름을 짝을 지어 표시
extra_attribs=['rooms_per_hhold', 'pop_per_hhold', 'bedrooms_per_room']

# 범주형 변수의 각 카테고리도 중요도를 탐색할 변수로 포함
cat_encoder=full_pipeline.named_transformers_['cat']
cat_one_hot_attribs=list(cat_encoder.categories_[0])

# 모든 변수들을 합쳐서 각 변수의 중요도와 짝을 지어 정렬
attributes=num_attribs+extra_attribs+cat_one_hot_attribs
sorted(zip(feature_importances,attributes),reverse=True)

 

 

 

 

6. 테스트 세트로 최종 모델 평가

pred=forest_reg.predict(test)

 -위에서 train 데이터셋으로 훈련시킨 랜덤 포레스트 모델에 test 데이터셋을 넣으면 test 데이터셋에 대한 예측값이 pred변수에 저장됨

+ Recent posts