핸즈온머신러닝&딥러닝

캘리포니아 주택 가격 예측2

threegopark 2021. 5. 16. 14:46
728x90
  • 사용자 정의 변환기 생성
from sklearn.base import BaseEstimator, TransformerMixin

rooms_ix, bedrooms_ix, population_ix, households_ix = 3,4,5,6

class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
    def __init__(self,add_bedrooms_per_room = True):
        self.add_bedrooms_per_room = add_bedrooms_per_room
    def fit(self, X, y=None):
        return self
    def transform(self,X):
        rooms_per_household = X[:,rooms_ix] / X[:, households_ix]
        population_per_household = X[:, population_ix] / X[:, households_ix]
        if self.add_bedrooms_per_room:
            bedrooms_per_room = X[:,bedrooms_ix] / X[:,rooms_ix]
            return np.c_[X,rooms_per_household, population_per_household, bedrooms_per_room]
        
        else:
            return np.c_[X, rooms_per_household, population_per_household]
        
attr_adder =  CombinedAttributesAdder(add_bedrooms_per_room = False)
housing_extra_attribs = attr_adder.transform(housing.values)

이전 포스팅에서 언급했듯이 baseestimator 상속 시, *args 나 **kwargs를 지정하지 않았을 경우 자동으로 하이퍼 파라미터 튜닝에 필요한 두 메서드 get_params()과 set_params()를 추가로 얻게된다.

이러한 사용자 정의 변환기를 생성할 경우 더 많은 하이퍼파라미터 조합을 시행하여 최상의 조합을 찾는 것에 도움을 준다.

 

 

특성 스케일링

 

  • 트리 기반 알고리즘을 제외하고 대부분의 머신러닝 알고리즘은 입력 숫자 특성들의 스케일에 민감함. ex) 특성 1 : 6~30000    //  특성 2 : 0~1 이러한 경우 각 특성의 스케일링 필요

 

  • min-max 스케일링 (정규화)

        1. normalization, 정규화

        2. 0 ~ 1 범위로 스케일링

        3. (data-min) / (max-min)

        4. 사이킷런의 MinMaxScaler 변환기 (feature_range 매개변수 설정으로 0~1 이외의 값 설정 가능)

 

  • 표준화

        1. (data-mean) / 표준편차   -->   분산 1이 되도록

        2. 상한과 하한 존재 x   -->   민감한 알고리즘에서는 문제 but 이상치에 영향을 덜 받음

        3. StandardScaler 변환기

 

 

변환 파이프라인

 

  • 연속된 변환을 순서대로 처리할 수 있도록 도와주는 Pipeline 클래스
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

num_pipeline = Pipeline([
#1단계 : 누락값 -> median 대체
    ('imputer', SimpleImputer(strategy="median")),
    
#2단계 : 사용자 정의 변환기 활용, 하이퍼파라미터의 조합 도출    
    ('attribs_adder', CombinedAttributesAdder()),
    
#3단계 : 표준화    
    ('std_scaler', StandardScaler()),
])

housing_num_tr = num_pipeline.fit_transform(housing_num)
  • 입력 : 연속된 단계를 나타내는 이름/추정기 쌍의 목록
  • 출력 : fit_transform() 메서드 포함

 

각 열마다 사용자 정의 함수를 적용하여 모든 열을 처리하는 변환기

 

  • 앞서 만든 파이프라인 활용한 ColumnTransformer 클래스
  • drop : 열 삭제
  • passthrough : 변환 적용 x 그냥 지나감
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),
])

housing_prepared = full_pipeline.fit_transform(housing)

 

 

 

모델 선택과 훈련

 

 

훈련 세트에서 훈련하고 평가하기

 

from sklearn.linear_model import LinearRegression

lin_reg = LinearRegression()
lin_reg.fit(housing_prepared, housing_labels)
some_data = housing.iloc[:5]
some_labels = housing_labels.iloc[:5]
some_data_prepared = full_pipeline.transform(some_data)

print("예측 : ", lin_reg.predict(some_data_prepared))
print("예측 : ", list(some_labels))

  • 첫 번째 예측은 40% 가까이 벗어남 --> 정확한 예측은 아님

 

  • mean_square_error 함수를 사용해 회귀모델의 RMSE 측정
from sklearn.metrics import mean_squared_error

housing_predictions = lin_reg.predict(housing_prepared)
lin_mse = mean_squared_error(housing_labels, housing_predictions)
lin_rmse = np.sqrt(lin_mse)
lin_rmse

$68,628의 좋지 못한 예측오차 발생 --> 과대적합 or 과소적합 의심

 

==> 다른 모델 적용, 규제추가 or 완화, 데이터 추가를  활용하여 좋지 못한 점수를 향상시키기

 

 

 

성능 향상을 위한 예시 1. (의사결정 나무를 활용해보자)

 

  • 과소적합 발생 시 : 규제를 완화하거나, 더 좋은 특성을 활용, or 더 강력한 모델 활용
  • 위의 예시가 과소적합이라는 가정 하에 더욱 강력한 모델인 결정나무 모델을 활용하여 재시도
from sklearn.tree import DecisionTreeRegressor

tree_reg = DecisionTreeRegressor()
tree_reg.fit(housing_prepared, housing_labels)

housing_predictions = tree_reg.predict(housing_prepared)
tree_mse = mean_squared_error(housing_labels, housing_predictions)
tree_rmse = np.sqrt(tree_mse)

tree_rmse

모델이 데이터에 너무 심하게 '과대적합'되어 0이라는 오차가 발생한 것임.

--> 이러한 과대적합을 방지하기 위해 k-fold 교차 검증 기능을 사용하여 데이터 셋을 나눠보자 (split_train_test보다 우수한 성능)

 

훈련 데이터를 10개의 폴드로 분리 --> 1~9까지의 폴드를 훈련에, 10번째 폴드를 검증에 --> 2~10까지의 폴드를 훈련에, 1번째 폴드를 검증에, --> 3,4,5,6,7,8,9,10,1 의 폴드를 훈련에, 2번째 폴드를 검증에 ------- 매번 다른 폴드를 선택해 평가에 사용하는 방식!!

 

from sklearn.model_selection import cross_val_score

scores = cross_val_score(tree_reg, housing_prepared, housing_labels,
                        scoring = "neg_mean_squared_error", cv=10)

tree_rmse_scores = np.sqrt(-scores)

def display_scores(scores):
    print("점수", scores)
    print("평균", scores.mean())
    print("표준편차", scores.std())
    
display_scores(tree_rmse_scores)

 

과대적합 방지

  • 1. 모델을 간단히
  • 2. 제한(규제)
  • 3. 더 많은 훈련 데이터

즉, 이렇게 여러 모델을 선정하여 시도해보고 가장 높은 점수의 모델을 선택한다.

 

 

 

적절한 모델 선정 후, 모델 세부 튜닝

 

  • 그리드 탐색

수동 or 자동으로 하이퍼파라미터를 조정해가며 최적의 조건을 찾는 방법

#랜덤포레스트 모델에 대해 그리드 탐색 적용

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



param_grid = [
    {'n_estimators' : [3,10,30],  'max_features' : [2,4,6,8]},
    {'bootstrap' : [False], 'n_estimators' : [3,10], 'max_features' : [2,3,4]},
]

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)

 

grid_search.best_estimator_

 

cvres = grid_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
    print(np.sqrt(-mean_score), params)

49897 로 이전에 시행하였을 때 발생한 오차보다 낮은 오차를 얻을 수 있다. (해당 파라미터가 최적이라는 의미)