728x90

오늘 진도 나간 부분은 아래 순으로 진행했습니다.

 

step 1) 피처중요도가 작았던 날씨 데이터를 활용한 새로운 파생변수 만들기

-> 처음에는 전체 데이터를 가지고 주말,평일을 나눠볼려고 시도했습니다. 그래서 군집분석을 해봤는데, 시도할수록 

2개로 군집 분석해도 이 의미가 과연 주말과 평일을 나누는 기준인가? 를 모르겠다는 겁니다 ㅎ;; 시도해 보고 나니 깨달았네요, 그대신 얻은 게 있다면 중요도가 낮은 변수들을 가지고 군집 분석을 통해 파생변수를 시도 했다는 것입니다. 제가 알수 없는 날씨 데이터간의 의미를 알아보는 시간이었습니다. 제 생각이지만 예를들어, 해당 군집 변수를 통해서 비가 내렸는지 , 눈이 내렸는지, 매우 더웠는지 등 알수 있지 않을까 생각해 봅니다(뇌피셜ㅎㅎ;;) 아래는 제가 책 보면서 실습해본 내용들입니다. 여러 실험을 해봤는데 .. 그닥 유의미한 변수를 뽑아 내지 못해서 사용하진 않을 것같습니다 ㅠ

 

*KMeans

#Train

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples,silhouette_score

recent_value = float('-inf')
for i in range(2,20):
  kmean = KMeans(n_clusters=i,random_state=2021)
  kmean.fit(X_features_log)

  #실루엣 평가
  score_samples = silhouette_samples(X_features_log, kmean.labels_)

  #실루엣 평균값
  # np.mean(train['silhouette_coeff'].values)
  print('n_clusters:',i,"평균값",np.mean(score_samples))
  

  if recent_value < np.mean(score_samples):
    recent_value = np.mean(score_samples)
    train['cluster'] = kmean.labels_

 

n_clusters: 2 평균값 0.30259347606298753
n_clusters: 3 평균값 0.22084565811856927
n_clusters: 4 평균값 0.216332733050493
n_clusters: 5 평균값 0.20117735495570135
n_clusters: 6 평균값 0.18407185642824536
n_clusters: 7 평균값 0.17967102086096723
n_clusters: 8 평균값 0.18385313419854404
n_clusters: 9 평균값 0.18900846443879946
n_clusters: 10 평균값 0.192206439445478
n_clusters: 11 평균값 0.19231478574253635
n_clusters: 12 평균값 0.19283175071206277
n_clusters: 13 평균값 0.18663087129390746
n_clusters: 14 평균값 0.18418271434686048
n_clusters: 15 평균값 0.1842834634207479
n_clusters: 16 평균값 0.19119926923768746
n_clusters: 17 평균값 0.1923920182110372
n_clusters: 18 평균값 0.19543748089010413
n_clusters: 19 평균값 0.19708363804370685

 

train['cluster']

 

0       1
1       1
2       0
3       1
4       0
       ..
1454    0
1455    0
1456    0
1457    0
1458    0
Name: cluster, Length: 1459, dtype: int32

 

* MeanShift

from sklearn.cluster import MeanShift
from sklearn.cluster import estimate_bandwidth

best_bandwidth = estimate_bandwidth(X_features_log)

meanshift = MeanShift(bandwidth=best_bandwidth)
cluster_labels= meanshift.fit_predict(X_features_log)
train['cluster'] = cluster_labels 
print('cluster labels 유형:',np.unique(cluster_labels))
cluster labels 유형: [0 1]

 

*GMM(Gaussian Mixture Model)

from sklearn.mixture import GaussianMixture

gmm = GaussianMixture(n_components=2, random_state=2021).fit(X_features_scaled)
gmm_cluster_labels = gmm.predict(X_features_scaled)

train['cluster'] = gmm_cluster_labels
print('gmm labels 유형:',np.unique(gmm_cluster_labels))

gmm_test = GaussianMixture(n_components=2, random_state=2021).fit(X_features_test_scaled)
gmm_cluster_test_labels = gmm.predict(X_features_test_scaled)

test['cluster'] = gmm_cluster_test_labels
gmm labels 유형: [0 1]

 

*DSCAN

from sklearn.cluster import DBSCAN

dbscan = DBSCAN(eps = 0.4 , min_samples=12, metric='euclidean')
dbscan_labels = dbscan.fit_predict(X_features_log)

train['cluster'] = dbscan_labels
print('gmm labels 유형:',np.unique(dbscan_labels))
dbscan labels 유형: [-1  0  1]

-1로 군집된 것들은 군집이 되지 못한 값들입니다. 따라서 0과 1로 군집이 되었음을 알수 있습니다. -1이 있다고 해서 잘못 군집 된 것이 아니라고 합니다.

 

 

 

 

step 2) rfecv 사용한 최적 변수 선택

from sklearn.feature_selection import RFECV

model = xgb.XGBRegressor(gamma=0.239148938489844, max_depth=2, min_child_weight=1, n_estimators=200,n_jobs=-1)
rfecv = RFECV(estimator=model, step=1, scoring='neg_mean_squared_error')
rfecv.fit(X_train[x_columns], y_train)
rfecv.transform(X_train[x_columns])
rfecv_colname =[colname for idx,colname in enumerate(X_train.columns) if rfecv.ranking_[idx]==1]
print(rfecv_colname)

 

['hour', 'hour_bef_temperature', 'hour_bef_precipitation', 'hour_bef_humidity', 'hour_bef_visibility', 'hour_bef_ozone', 'hour_bef_pm10', 'cluster']

rfecv를 간략하게 말하자면, 변수 하나씩 모두 투입하여 가장 최적의 변수를 선택하는 기법입니다.

grid를 통해 가장 성능이 좋게 나왔던 모델로 rfecv를 진행했습니다. 그 결과 위 컬럼들을 알수 있었고 해당 컬럼만을 가지고 다시 돌려본 결과 좀 더 나빠졌습니다. 물론 데이콘에 직접 제출하기 전까지는 단정짓긴 힘들지만 하루에 제출 3번이 한계라서 막 제출 할수 없어서 피드백이 좀 아쉽습니다. 하여튼 성능개선을 보이지 않아 이 방법도 일단 보류하게 되었습니다.

 

 

 

Last 3) 원 핫인코딩 및 target log화

# 데이터 분리
x_train = train[[column for column in train.columns if column != 'count']]
y_train = train['count']

# target log
y_target_log = np.log1p(y_train)

#원핫 인코딩
x_train = pd.get_dummies(x_train, columns=['hour'])
test = pd.get_dummies(test, columns=['hour'])

X_train, X_test, y_train, y_test = train_test_split(x_train, y_target_log, test_size=0.3, random_state=2021)

target 변수가 전에 봤을때 살짝 비대칭 이었지만 정규분포이라는 통계량을 받았었습니다. 하지만 해보지 않으면 모르는법. 할수 있는 것들을 모두 해보자는 생각에 일단 target에 log화 시켜서 정규분포에 가깝게 만들었습니다. 그리고 기존에 피처 중요도에서 hour가 가장 크게 나왔습니다. hour가 0~23까지 있는데, 숫자형 값으로 입력하게 되면 해당 값에 크게 영향을 받게 되어서 피처 중요도가 높게 찍히게 됩니다. 카테고리 형은 원-핫 인코딩을 적용하여 변환해 줘야 합니다. 사실 이 부분을 간과하고 있었습니다..;

 

그래서 현재 target log 했을때의 성능 vs 원핫 + log 했을때의 성능

이 둘을 비교 하고 싶은데, 앞서 제출 기회 3번을 이미 쓴지라 제출 자료만 미리 만들어 놨습니다 

 

그런데, 확실히 원핫인코딩 한 후에 성능이 꽤 좋아진 걸 확인 할수 있었습니다. 제출해 봐야 알겠지만 자세한 내용은 아래를 참고하겠습니다.

#그리드 서치
models = [
    ('ridge', lm.Ridge()),
    ('lasso', lm.Lasso()),
    ('elastic', lm.ElasticNet()),
    ('LassoLars', lm.LassoLars()),
    # ('LogisticRegression', lm.LogisticRegression()),
    # ('SGDRegressor', lm.SGDRegressor()),
    ('xgboost', xgb.XGBRegressor()),
    ('lgb', lgb.LGBMRegressor()),
    ('knn', KNeighborsRegressor(n_jobs = -1) ),
    # ('rf', RandomForestRegressor(n_jobs = -1)),
    ('dt',  DecisionTreeRegressor()),
    ('ada', AdaBoostRegressor())
]

params = {
    'ridge': {
        'alpha': [0.01, 0.1, 1.0, 10, 100],
        'fit_intercept': [True, False],
        'normalize': [True, False],
    },
    'lasso': {
        'alpha': [0.1, 1.0, 10],
        'fit_intercept': [True, False],
        'normalize': [True, False],
    },
    'elastic': {
        'alpha': [0.1, 1.0, 10],
        'normalize': [True, False],
        'fit_intercept': [True, False],
    },
    'LassoLars': {
        'alpha': [0.1, 1.0, 10],
        'normalize': [True, False],
        'fit_intercept': [True, False],
    },
    # 'LogisticRegression': {
    #     'penalty': ['l1', 'l2'],
    #     'C': [0.01,0.1, 1.0, 10,100],
    #     'fit_intercept': [True, False],
    # },
    # 'SGDRegressor': {
    #     'penalty': ['l1', 'l2'],
    #     'alpha': [0.001, 0.01, 0.1, 1.0, 10, 100],
    #     'fit_intercept': [True, False],
    # },
    'xgboost': {
        "gamma": uniform(0, 0.5).rvs(3),
        "max_depth": range(2, 7), 
        "n_estimators": [100,200,300] 
    },
    'lgb': {
        "gamma": uniform(0, 0.5).rvs(3),
        "max_depth": range(2, 7), 
        "n_estimators": [100,200,300,400,500] 
    },
    'knn': {
        "n_neighbors": range(2,7),
        },
    # 'rf': {
    #     "max_depth": range(2, 5),
    #     "min_samples_split": range(2, 5),
    #     "min_samples_leaf": range(2, 5), 
    #     "n_estimators": [100,200,300],
    #     },
    'dt': {
        "max_depth": range(2, 5),
        "min_samples_split": range(2, 5),
        "min_samples_leaf": range(2, 5), 
        },
    'ada': {
        "n_estimators": [40,50,60]
    }
}

best_model, best_score = None, float('inf')
# x_columns = ['hour','hour_bef_temperature','hour_bef_precipitation']
x_columns = X_train.columns
for model_name, model in models:
    param_grid = params[model_name]
    grid = GridSearchCV(model, cv=5, n_jobs=-1, param_grid=param_grid)
    grid = grid.fit(X_train[x_columns], y_train)

    model = grid.best_estimator_
    predictions = model.predict(X_test[x_columns])

    y_test_exp = np.expm1(y_test)
    predictions_exp = np.expm1(predictions)
    score = evaluate(y_test_exp, predictions_exp)['mse'][0]

    print(model_name, score)

    if score < best_score:
        best_score=score
        best_model = model

 

ridge 1605.0139030409086
lasso 4164.706326162681
elastic 4106.425576729229
LassoLars 4164.84687453161
[12:29:45] WARNING: /workspace/src/objective/regression_obj.cu:152: reg:linear is now deprecated in favor of reg:squarederror.
xgboost 1796.044492222476
lgb 1829.1214004774263
knn 5546.570322907046
dt 3549.1672544535268
ada 6018.963056798382

해당 결과 값은 MSE입니다. 줄곧 베스트 모델이 xgb 였는데, 원핫 인코딩 하고나서는 성능도 개선 되면서 ridge가 가장 좋은 모델로 선택되었습니다. 

best_model
Ridge(alpha=0.01, copy_X=True, fit_intercept=True, max_iter=None,
      normalize=True, random_state=None, solver='auto', tol=0.001)

 

 

여기까지가 오늘 진도나간 부분입니다. 다음 시간에 더 개선된 내용을 가지고 업로드 하겠습니다.

 

 

 

728x90

+ Recent posts