728x90

오늘은 깃허브에 대해 공부했습니다. 그 전부터 몇번 시도는 했었으나 branch는 뭐고 commit은 뭔지 도통 감이 잡히지 않아서 나중에 하기로 미룬지가 거의 반년이 넘어 갔습니다. 공부 할수록 깃허브의 중요성을 조금씩 알게 되었고, 프로젝트를 만들면서 버전관리에 중요성을 깨달았는지라 오늘 시간을 내어봤습니다.

 

개념을 빠르게 배우기 위해서 유튜브 강의를 시청했습니다. (아래는 해당 링크 입니다)

https://www.youtube.com/watch?v=-27WScuoKQs&t=751s 

영상 총 길이가 1시간 좀 넘었는데, 좋은 설명 덕분에 이해하기 수월했습니다. 현재는 배운내용을 토대로 commit과 push를 연습 중에 있습니다. 

 

이 과정에서 몇 시간동안 제 발목을 잡은 에러가 있었습니다. 소스트리에서 push를 누르니까 비밀번호를 누르라고 해서 깃허브 비번을 입력했더니 에러가 발생했습니다. 다시 푸시를 눌러도, 재설치를 해봐도 에러가 발생했습니다. 알고 보니 해당 비밀번호는 깃허브 홈페이지에서 할당받은 토큰을 넣는 것으로 변경되었다고 합니다.

 

구글링하면서 이것저것 시도하다가 ssh로 시도하게 되었습니다. 제가 참고했던 ssh 적용방법 블로그는 아래에 걸어 두겠습니다.

https://happysalmon.tistory.com/3

 

(소스 트리) 깃랩을 이용한 소스트리 SSH key 등록 방법

소스 트리 다운로드와 깃 랩(Gitlab) SSH key 키 등록 방법, 프로젝트 클론 하는 방법에 대해서 알아보도록 하겠습니다. 준비 소스 트리 링크 다운로드 및 회원가입 2. 깃 랩(Gitlab) 링크 회원가입 순

happysalmon.tistory.com

 

그런데, 위 과정으로만 했더니 또 에러가 발생했습니다. 에러 메세지를 읽어 보니까 저는 좀 다른 방식으로 해야했음을 알게 되었어요.

블로그에서는 위 이미지처럼 ssh 클라이언트를 OpenSSH로 변경하도록 안내하고 있습니다. ssh키는 내가 ssh키를 저장해 놓은 위치입니다. 아무튼 이 과정대로 따라가니까 에러가 발생했습니다.

 

저 같은 경우는 나머지 모두 같지만 딱 하나 다르게 설정하니까 push가 되었습니다. 해당 블로그가 틀린 정보를 소개해준게 아닙니다 다른 블로그들 보면 같은 방법으로 안내를 하고 있는데, 저는 좀 다른 케이스 인것 같더라구요. 정확한 원인은 잘 모르겠지만 나도 이제 깃허브를 사용할수 있다는 생각에 좋았답니다 

 

이참에 현재 진행중인 따릉이 프로젝트도 버전관리 하고 싶어서 코랩으로 깃허브 연동까지 진행했습니다. 코랩의 깃허브 사본저장만 누르면 끝이라서 상당히 간편했습니다.

태양열 에너지 예측은 코드가 뒤죽박죽에다가 EDA가 제외가 된 터라(EDA는 파일이 별도로 존재함) 제외하고 업로드 해봤습니다.  

 

 

 

 

 

 

728x90
728x90

target 분포

저번 포스팅 마지막에는 count의 분포와 비슷한 파생변수를 만든다는 말 끝으로 끝냈었습니다. 예고 했듯이 제 나름대로 target 변수를 설명해줄 파생변수를 2가지 만들어 봤습니다. 그 외로 몇가지 더 실험해 보았는데 이는 아래 주요 요약에 적어 놓았습니다. 

 

*주요 요약

  • target 분포를 설명해줄 파생변수 2가지
  • 변수들의 왜곡 확인 & target 변수의 이상치 제거
  • 스태킹 모델 

 

1. target 분포를 설명해줄 파생변수 2가지

 

1.1 시간별 평균 이용 수

train_df = train.copy()
test_df = test.copy()
train_df['cue'] = 0
test_df['cue'] = 1
df = pd.concat([train_df,test_df],axis=0).reset_index(drop=True)

# 전체 데이터 중 train에 해당하는 행 추출
train_data = df.query('cue=="0"').reset_index(drop=True)

df['hour_mean']=1

#각 시간별 인덱스 추출
index00 = df.query('hour=="0"').index
index01 = df.query('hour=="1"').index
index02 = df.query('hour=="2"').index
index03 = df.query('hour=="3"').index
index04 = df.query('hour=="4"').index
index05 = df.query('hour=="5"').index
index06 = df.query('hour=="6"').index
index07 = df.query('hour=="7"').index
index08 = df.query('hour=="8"').index
index09 = df.query('hour=="9"').index
index10 = df.query('hour=="10"').index
index11 = df.query('hour=="11"').index
index12 = df.query('hour=="12"').index
index13 = df.query('hour=="13"').index
index14 = df.query('hour=="14"').index
index15 = df.query('hour=="15"').index
index16 = df.query('hour=="16"').index
index17 = df.query('hour=="17"').index
index18 = df.query('hour=="18"').index
index19 = df.query('hour=="19"').index
index20 = df.query('hour=="20"').index
index21 = df.query('hour=="21"').index
index22 = df.query('hour=="22"').index
index23 = df.query('hour=="23"').index

# 각 시간별 평균값을 "hourmean" 변수에 대입
df.iloc[index00,-1] = train_data.query('hour=="0"')['count'].mean()
df.iloc[index01,-1] = train_data.query('hour=="1"')['count'].mean()
df.iloc[index02,-1] = train_data.query('hour=="2"')['count'].mean()
df.iloc[index03,-1] = train_data.query('hour=="3"')['count'].mean()
df.iloc[index04,-1] = train_data.query('hour=="4"')['count'].mean()
df.iloc[index05,-1] = train_data.query('hour=="5"')['count'].mean()
df.iloc[index06,-1] = train_data.query('hour=="6"')['count'].mean()
df.iloc[index07,-1] = train_data.query('hour=="7"')['count'].mean()
df.iloc[index08,-1] = train_data.query('hour=="8"')['count'].mean()
df.iloc[index09,-1] = train_data.query('hour=="9"')['count'].mean()
df.iloc[index10,-1] = train_data.query('hour=="10"')['count'].mean()
df.iloc[index11,-1] = train_data.query('hour=="11"')['count'].mean()
df.iloc[index12,-1] = train_data.query('hour=="12"')['count'].mean()
df.iloc[index13,-1] = train_data.query('hour=="13"')['count'].mean()
df.iloc[index14,-1] = train_data.query('hour=="14"')['count'].mean()
df.iloc[index15,-1] = train_data.query('hour=="15"')['count'].mean()
df.iloc[index16,-1] = train_data.query('hour=="16"')['count'].mean()
df.iloc[index17,-1] = train_data.query('hour=="17"')['count'].mean()
df.iloc[index18,-1] = train_data.query('hour=="18"')['count'].mean()
df.iloc[index19,-1] = train_data.query('hour=="19"')['count'].mean()
df.iloc[index20,-1] = train_data.query('hour=="20"')['count'].mean()
df.iloc[index21,-1] = train_data.query('hour=="21"')['count'].mean()
df.iloc[index22,-1] = train_data.query('hour=="22"')['count'].mean()
df.iloc[index23,-1] = train_data.query('hour=="23"')['count'].mean()

시간 별 count 평균값을 활용하여 hour_mean 변수를 생성했습니다. 해당 변수를 수행한 결과, hour 변수와 연관성이 컸습니다. feature 중요도에서 hour값을 빼면 hour_mean 값이 크게 증가 한걸 확인했습니다. 다만 hour_mean 중요도가 hour 중요도보다 약 9% 더 크게 나왔습니다. (이미지가 아까 있었는데 다른 실험들 하느라 없어졌네요..;)

 

df.tail()
	id	hour	hour_bef_temperature	hour_bef_precipitation	hour_bef_windspeed	hour_bef_humidity	hour_bef_visibility	hour_bef_ozone	hour_bef_pm10	hour_bef_pm2.5	count	cue	hour_mean
2169	2148	1	24.6	0.0	2.4	60.0	1745.0	0.023833	46.0	30.25	NaN	1	47.606557
2170	2149	1	18.1	0.0	1.0	55.0	2000.0	0.027000	30.0	20.25	NaN	1	47.606557
2171	2165	9	23.3	0.0	2.3	66.0	1789.0	0.020000	17.0	15.00	NaN	1	93.540984
2172	2166	16	27.0	0.0	1.6	46.0	1956.0	0.032000	40.0	26.00	NaN	1	169.100000
2173	2177	8	22.3	0.0	1.0	63.0	1277.0	0.007000	30.0	24.00	NaN	1	136.688525

 

 

1.2 hour_bef_precipitation에 따른 평균 이용 수

df['precipitation_mean'] = 1
index0 = df.query('hour_bef_precipitation=="0.0"').index
index1 = df.query('hour_bef_precipitation=="1.0"').index

df.iloc[index0,-1] =train_data.query('hour_bef_precipitation=="0.0"')['count'].mean()
df.iloc[index1,-1] =train_data.query('hour_bef_precipitation=="1.0"')['count'].mean()

비의 유무에 따른 평균 이용 수 변수를 생성했습니다. 비가 내린 날과 내리지 않는 날을 명확하게 구분하려는 의도로 만들었지만 영향력이 0 였습니다. 

 

 

 

 

2. 변수들의 왜곡 확인 & target 변수의 이상치 제거

 

2.1 변수들의 왜곡 확인 (보통 1 이상일때 왜곡이 있다고 판정하여 log 변환 실시)

# 변수들의 왜곡 확인
from scipy.stats import skew
feature_df = df.drop(['id'	,'hour','count','cue','hour_bef_precipitation'], axis=1)
feature_index = feature_df.dtypes[feature_df.dtypes != 'object'].index 
feature_index = feature_df.dtypes[feature_df.dtypes != 'object'].index 
skew_features = feature_df[feature_index].apply(lambda x: skew(x))

#shew(왜곡) 저도가 1 이상인 칼럼만 추출
skew_features_top = skew_features[skew_features>1]
print(skew_features_top.sort_values(ascending=False))

 

hour_bef_pm10     2.645937
hour_bef_pm2.5    1.387923
dtype: float64

위에 두 변수에서 왜도가 있음을 나타내고 있습니다. 하지만 해당 변수들은 학습에서 사용하지 않기 때문에 pass 했습니다.

 

 

 

2.2 target 변수의 이상치 제거

plt.figure(figsize=(15,10))
sns.boxplot(x='hour',y='count', data=train)
plt.show()

 

target 변수 분포에서 가장 특징점은 출퇴근 시간에서 수요가 급증한다는 점이다. 해당 특징을 잘 살릴수록 성능에 큰 도움이 될거라 예상됩니다.

  • count의 퇴근 시간대 분포를 보면 이상치가 존재한다. 이는 휴일,주말에 측정한 것으로 추측되므로 이를 제거한다
  • 2017년 5월 휴일은 석가탄신일, 어린이날, 19대 선거 3일이다. 시간별 count 이상치 갯수와 비슷하다
  • 휴일 변수는 test의 count값이 없기 때문에 만들지 못한다.
#18시
train[train['hour']==18]['count']<100  # 19,1035,1113
# #19시
train[train['hour']==19]['count']<50  # 110, 306, 713

위 코드 실행 결과, 옆에 주석으로 아웃라이어 인덱스를 표시해 뒀습니다. 100과 50은 상자그림을 통해서 간단하게 설정한 겁니다. 해당 인덱스를 제거하도록 하겠습니다.

del_index = [19,1035,1113,110,306,713]
df.drop(del_index, axis=0, inplace =True)

 

 

3. CV 기반의 스태킹

#보류 -> 그리드 서치를 통해 결정하기
#개별 model 생성
knn=KNeighborsRegressor(n_jobs = -1)
rf = RandomForestRegressor(n_jobs = -1, random_state=2021)
dt = DecisionTreeRegressor(random_state=2021)
xgb = XGBRegressor(verbosity = 0, random_state=2021)
ada = AdaBoostRegressor(random_state=2021)
ridge=lm.Ridge()
lasso=lm.Lasso()
final = lm.Ridge()
lgb_reg=lgb.LGBMRegressor()

 

def print_best_params(model, params):
  best_model, best_score = None, float('inf')
  grid_model = GridSearchCV(model,param_grid=params,
                            scoring='neg_mean_squared_error',cv=5)
  grid_model.fit(X_train,Y_train)
  # predictions = grid_model.predict(X_test)
  # score = evaluate(Y_test, predictions)['mse'][0]
  print("model name is {0},Grid best score:{1}, Grid best_params_:{2} ".format(model.__class__.__name__,grid_model.best_score_,grid_model.best_params_))

 

#param 설정
knn_params ={"n_neighbors": range(2,7)}
rf_params={"max_depth": range(2, 5),"min_samples_split": range(2, 5),"min_samples_leaf": range(2, 5), "n_estimators": [100,200,300]}
dt_params={"max_depth": range(2, 5),"min_samples_split": range(2, 5),"min_samples_leaf": range(2, 5)}
xgb_params={"gamma": uniform(0, 0.5).rvs(3),"max_depth": range(2, 7), "n_estimators": [100,200,300]}
ada_params={"n_estimators": [40,50,60]}
lgb_params={"gamma": uniform(0, 0.5).rvs(3),"max_depth": range(2, 7), "n_estimators": [100,200,300,400]}
Ridge_params={'alpha': [0.01, 0.1, 1.0, 10, 100],'fit_intercept': [True, False],'normalize': [True, False]}
lasso_params={'alpha': [0.1, 1.0, 10],'fit_intercept': [True, False],'normalize': [True, False]}

 

print_best_params(knn,knn_params)
print_best_params(rf,rf_params)
print_best_params(dt,dt_params)
print_best_params(ada,ada_params)
print_best_params(xgb,xgb_params)
print_best_params(lgb_reg,lgb_params)
print_best_params(ridge,Ridge_params)
print_best_params(lasso,lasso_params)
model name is KNeighborsRegressor,Grid best score:-3458.5501989157733, Grid best_params_:{'n_neighbors': 4} 
model name is RandomForestRegressor,Grid best score:-1715.3496565899343, Grid best_params_:{'max_depth': 4, 'min_samples_leaf': 3, 'min_samples_split': 2, 'n_estimators': 100} 
model name is DecisionTreeRegressor,Grid best score:-2033.204198431899, Grid best_params_:{'max_depth': 4, 'min_samples_leaf': 3, 'min_samples_split': 2} 
model name is AdaBoostRegressor,Grid best score:-2149.0152774244843, Grid best_params_:{'n_estimators': 40} 
model name is XGBRegressor,Grid best score:-1477.5776373356193, Grid best_params_:{'gamma': 0.12889993895241564, 'max_depth': 4, 'n_estimators': 100} 
model name is LGBMRegressor,Grid best score:-1524.0959447354717, Grid best_params_:{'gamma': 0.273931886786571, 'max_depth': 6, 'n_estimators': 100} 
model name is Ridge,Grid best score:-1751.7217163613059, Grid best_params_:{'alpha': 0.1, 'fit_intercept': True, 'normalize': False} 
model name is Lasso,Grid best score:-1754.1340908572297, Grid best_params_:{'alpha': 0.1, 'fit_intercept': True, 'normalize': False}

 

knn=KNeighborsRegressor(n_jobs = -1)
rf = RandomForestRegressor(max_depth= 4, min_samples_leaf= 2, min_samples_split= 2,n_jobs = -1, random_state=2021)
dt = DecisionTreeRegressor(max_depth= 4, min_samples_leaf= 4, min_samples_split= 2,random_state=2021)
xgb = XGBRegressor(gamma= 0.25883224322873616, max_depth= 4, n_estimators= 100, verbosity = 0, random_state=2021)
ada = AdaBoostRegressor(n_estimators= 40,random_state=2021)
ridge=lm.Ridge(alpha= 0.01, fit_intercept= True, normalize= True)
lasso=lm.Lasso(alpha= 1.0, fit_intercept= True, normalize= False)

#최종 메타 모델
lgb_reg=lgb.LGBMRegressor(gamma= 0.08826298344672961, max_depth= 4)

 

def get_stacking_base_datasets(model, x_train, y_train, test, n_folds):
 
  # 지정된 n_folds 값으로 kFold 생성
  kf = KFold(n_splits=n_folds, shuffle=False , random_state=2021)
  #추후에 메타 모델이 사용할 학습 데잍 반환을 위한 넘파이 배열 초기화
  train_fold_pred = np.zeros((x_train.shape[0],1)) #(1459,1)
  test_fold_pred = np.zeros((test.shape[0],n_folds)) #(715,5)
  print(model.__class__.__name__,"model 시작")

  for folder_counter,(train_index,valid_index) in enumerate(kf.split(x_train)):
    # print('train_index:',train_index,'valid_index:',valid_index)
    # print('valid 갯수:',len(valid_index))
    # print('\t 폴드 세트:', folder_counter,"시작")

    #입력된 학습 데이터에서 기반 모델이 학습/예측할 폴드데이터 세트 추출
    x_tr = x_train[train_index]
    y_tr = y_train[train_index]
    x_te = x_train[valid_index]
    
    #폴드 세트 내부에서 다시 만들어진 학습데이터로 기반 모델의 학습 수행
    model.fit(x_tr,y_tr)
    
    #폴드 세트 내부에서 다시 만들어진 검증 데이터로 기반 모델 예측 후 데이터 저장
    train_fold_pred[valid_index,:]=model.predict(x_te).reshape(-1,1)
    
    #입력된 원본 테스트 데이터를 폴드 세트내 학습된 기반 모델에서 예측 후 데이터 저장
    test_fold_pred[:,folder_counter]=model.predict(test)

  #폴드 세트 내에서 원본테스트 데이터르 예측한데이터를 평균하여 테스트 데이터로 생성
  test_pred_mean = np.mean(test_fold_pred, axis =1).reshape(-1,1)

  #train_fold_pred는 최종 메타 모델이 사용하는 학습 데이터, test_pred_mean은 테스트 데이터
  return train_fold_pred, test_pred_mean

 

x_train_n=x_train.values
y_train_n=y_train.values
test_n=test.values
n_fold=5

knn_train,knn_test = get_stacking_base_datasets(knn,x_train_n,y_train_n,test_n,n_fold)
rf_train,rf_test = get_stacking_base_datasets(rf,x_train_n,y_train_n,test_n,n_fold)
xgb_train,xgb_test = get_stacking_base_datasets(xgb,x_train_n,y_train_n,test_n,n_fold)
# df_train,df_test = get_stacking_base_datasets(df,x_train_n,y_train_n,test_n,n_fold)
ada_train,ada_test = get_stacking_base_datasets(ada,x_train_n,y_train_n,test_n,n_fold)
KNeighborsRegressor model 시작
RandomForestRegressor model 시작
XGBRegressor model 시작
AdaBoostRegressor model 시작

 

stack_final_x_train=np.concatenate((knn_train,rf_train,xgb_train,ada_train),axis=1)
stack_final_x_test=np.concatenate((knn_test,rf_test,xgb_test,ada_test), axis=1)

 

lgb_reg.fit(stack_final_x_train,y_train) #원본 학습 label과 fit
stack_final=lgb_reg.predict(stack_final_x_test)
# evaluate(Y_test, stack_final)

 

스태킹관련 코드 내용들은 "머신러닝 완벽가이드"를 참고했습니다. 각 개별 모델의 최적 param을 찾은 뒤, 각 개별에서 실시한 train, valid 값을 np.concatenate 하여 최종 모델에서 다시 한번 학습과 예측을 실시합니다. 자세한 내용은 책을 통해 공부해 보시길 강추 드립니다. 해당 스태킹 결과는 내일이나 주중에 제출해 볼 계획입니다(제출 횟수 초과함)

 

728x90
728x90

지난 포스팅에 이어서 th가 2개 이상인 멀티에서는 Update가 어떻게 진행이 되는지 살펴보도록 합니다. 이해를 돕기 위해서 input data가 1개라는 가정하에 진행합니다.

눈에 띄는 점은 th가 2개 이기 때문에 따로 분리가 된다는 점이다. Loss까지 구할 수 있으며 이를 편미분 하게 된다면 아래처럼 계산이 된다.

이 값들을 이용하여 backward를 간편하게 구할수 있다. 빨간색이 backward 진행 상황을 나타낸다.

빨간색 backward 값을 받은 Z2-2은 th1과 th2 두 방향으로 보내줘야 하는데, 전달되는 값을 보면 초록색 부분이 각각 1이므로 -2(y-y^)값을 그대로 전달한다.

나머지 부분도 편미분을 전개하면 아래와 같이 정리할 수 있다.

만일 X가 한개가 아니라 m개라면 어떻게 표현할수 있을까? 아래 그림을 통해 알수 있다.

X가 m개 이므로 th도 m개 이다. th와 x를 곱해줘야 하므로 th벡터를 transpose해야한다. 구체적인 과정을 그림을 통해 알아보자.

X가 m개 까지 있을때(feature의 수 m개) 위쪽에서 진행했던 과정을 반복해 나가면 된다. 파란색 화살표 부분이 prediction 값이 될수 있으며, affine 부분에 해당한다.

빨간색 화살표는 모두 -2(y-y^)의 값을 동일하게 전달한다. 왜냐하면 편미분 값이 전부 1이기 때문이다. 자세한 내용은 아래를 참고하면 이해하기 쉽다.

동일하게 전달받은 값은 각각 th1 ~thm까지 각각 미분해주면 된다.

 

728x90
728x90

*주요 요약

1. hour_bef_ozone의 결측치 예측

2. 결측치 다수 존재하는 변수들은 미 사용하기로 판단함

 

 

* 본 내용

결측치가 많은 ozone, pm10, pm2.5를 예측하여 값을 채우려 시도하고 있습니다. 기존에는 각 시간, 변수 별 평균값으로 결측치를 채웠습니다. 하지만 이러한 방법은 정확성을 떨어뜨릴수 있습니다 게다가 그러한 결측치가 워낙 많다 보니까 새로운 방법을 찾아야 했습니다. ozone, pm10, pm2.5를 제외한 feature을 활용했습니다.(id, count 제외)

 

먼저, 사용될 X feature들의 결측치 먼저 처리해야 합니다. 결측치 수가 많지 않으므로 이는 기존에 만들어 사용했던 시간,변수별 평균값 함수를 사용하여 채웠습니다. 아래 step 1을 통해 상세하게 설명드립니다.

 

Step 1. nan_mean_fill 함수를 사용하여 X feature 채우기 (단, precipitation은 분류 이므로 이 또한 제외)

def nan_mean_fill(dataset):
  #결측치가 있는 컬럼
  nan_columns = [col for col in dataset.iloc[:,2:-1].columns.to_list() if col!='hour_bef_precipitation' and col!='hour_bef_ozone' and col!='hour_bef_pm10' and col!='hour_bef_pm2.5']

  #for 문을 통해 결측치를 각 시간대별 평균값으로 대체
  for col in nan_columns:
    hour_mean_value = dataset.groupby('hour').mean()[col]
    hour_mean_value.fillna(hour_mean_value.mean(),inplace=True) #회귀를 통해서 값 채우기 시도

    for nan_hour in dataset[dataset[col].isna()]['hour'].unique():
      
      #nan index 구하기
      index= dataset[dataset[col].isna()].loc[dataset['hour']==nan_hour,col].index

      #채우기 
      for idx in index:
        dataset.loc[idx, col] = hour_mean_value[nan_hour]
        
  print(dataset.isna().sum())
  return dataset

train = nan_mean_fill(train)
test= nan_mean_fill(test)

 

id                          0
hour                        0
hour_bef_temperature        0
hour_bef_precipitation      2
hour_bef_windspeed          0
hour_bef_humidity           0
hour_bef_visibility         0
hour_bef_ozone             76
hour_bef_pm10              90
hour_bef_pm2.5            117
count                       0
dtype: int64
id                         0
hour                       0
hour_bef_temperature       0
hour_bef_precipitation     1
hour_bef_windspeed         0
hour_bef_humidity          0
hour_bef_visibility        0
hour_bef_ozone            35
hour_bef_pm10             37
hour_bef_pm2.5            36

precipitation과 target으로 사용될 ozone,pm10, pm2.5를 제외한 나머지 변수들의 값을 채운걸 확인해 볼수 있습니다.

 

다음은 Logistic을 통해 precipitation의 결측치를 채우겠습니다. 참고로 precipitation은 비가 온다 안온다에 대한 데이터이며 0과1로 구성되어 있습니다.

 

 

 

step 2. precipitation 결측값 채우기

from sklearn.linear_model import LogisticRegression
from sklearn import metrics

#x,y 분리
precipitation_x_del = pd.concat([train,test], axis=0).iloc[:,1:-4].dropna()
cls_x_train = precipitation_x_del[[col for col in precipitation_x_del.columns if col!='hour_bef_precipitation']]
cls_y_train =precipitation_x_del['hour_bef_precipitation']

#data split
cls_X_train, cls_X_val, cls_Y_train, cls_Y_test = train_test_split(cls_x_train, cls_y_train, test_size = 0.3, shuffle = True, random_state = 2021)

# model fit
cls_model = LogisticRegression()
cls_model.fit(cls_X_train, cls_Y_train)

#predict
y_pred = pd.Series(cls_model.predict(cls_X_val))

# model evaluation
print("Accuracy:", metrics.accuracy_score(cls_Y_test, y_pred))
Accuracy: 0.9662576687116564

 

# 불필요한 변수 제거
hour_bef_precipitation_train_df = train[train['hour_bef_precipitation'].isna()].iloc[:,1:-4]
del hour_bef_precipitation_train_df['hour_bef_precipitation']
display(hour_bef_precipitation_train_df)

hour_bef_precipitation_test_df = test[test['hour_bef_precipitation'].isna()].iloc[:,1:-3]
del hour_bef_precipitation_test_df['hour_bef_precipitation']
display(hour_bef_precipitation_test_df)

#predict -> 비가 오지 않음(0) 
print(cls_model.predict(hour_bef_precipitation_train_df))
print(cls_model.predict(hour_bef_precipitation_test_df))

# 결측값 0으로 채우기
train['hour_bef_precipitation'].fillna({934:0, 1035:0}, inplace=True)
print(train.isna().sum())

test['hour_bef_precipitation'].fillna({653:0}, inplace=True)
print(test.isna().sum())

#predict -> 비가 오지 않음(0) 
print('train의 hour_bef_precipitation 예측값:',cls_model.predict(hour_bef_precipitation_train_df))
print("test의 hour_bef_precipitation 예측값:",cls_model.predict(hour_bef_precipitation_test_df))

 

	hour	hour_bef_temperature	hour_bef_windspeed	hour_bef_humidity	hour_bef_visibility
934	0	14.788136	1.965517	58.169492	1434.220339
1035	18	20.926667	3.838333	40.450000	1581.850000
hour	hour_bef_temperature	hour_bef_windspeed	hour_bef_humidity	hour_bef_visibility
653	19	26.110345	3.541379	47.689655	1561.758621
[0. 0.]
[0.]
id                          0
hour                        0
hour_bef_temperature        0
hour_bef_precipitation      0
hour_bef_windspeed          0
hour_bef_humidity           0
hour_bef_visibility         0
hour_bef_ozone             76
hour_bef_pm10              90
hour_bef_pm2.5            117
count                       0
dtype: int64
id                         0
hour                       0
hour_bef_temperature       0
hour_bef_precipitation     0
hour_bef_windspeed         0
hour_bef_humidity          0
hour_bef_visibility        0
hour_bef_ozone            35
hour_bef_pm10             37
hour_bef_pm2.5            36
dtype: int64
train의 hour_bef_precipitation 예측값: [0. 0.]
test의 hour_bef_precipitation 예측값: [0.]

결측값을 채우기 위한 분류 결과, 모두 비가 오지 않았다는 예측을 얻었을 수가 있었습니다. 따라서 precipitation 결측값에 모두 0을 채웠습니다. 

 

X feature들의 결측값을 모두 채웠으므로 ozone, pm10, pm2.5 각각을 Y_target으로 설정하여 예측을 해보록 하겠습니다. 

 

step3. ozone, pm10, pm2.5 결측값 예측

def evaluate(y_true, y_pred):
  y_true, y_pred = np.array(y_true), np.array(y_pred)
  mape= np.mean(np.abs((y_true-y_pred)/y_true))*100
  mae= mean_absolute_error(y_true,y_pred)
  mse= mean_squared_error(y_true,y_pred)
  score = pd.DataFrame([mape, mae, mse], index=['mape','mae','mse'], columns=['score']).transpose()
  return score

 

def Nan_prediction(targets):
  # train 데이터 만들기
  for target in targets:
    if target =='hour_bef_pm2.5':
       use_columns = ['hour', 'hour_bef_temperature', 'hour_bef_precipitation','hour_bef_windspeed', 'hour_bef_humidity', 'hour_bef_visibility','hour_bef_pm10',target]
    else:
      use_columns = ['hour', 'hour_bef_temperature', 'hour_bef_precipitation','hour_bef_windspeed', 'hour_bef_humidity', 'hour_bef_visibility',target]

    #feature, target data split
    without_isna = pd.concat([train,test],axis=0).loc[:,use_columns].dropna()
    X_feature = without_isna.iloc[:,:-1]
    Y_train = without_isna[target]

    X_train, X_test, Y_train, Y_test = train_test_split(X_feature,Y_train, test_size=0.3, random_state=2021)

    # 원본 train data 안에 nan 값에 해당하는 Feature 추출 
    ozone_isna_train_df= train[train[target].isna()]

    # 원본 test data 안에 nan 값에 해당하는 Feature 추출
    ozone_isna_test_df= test[test[target].isna()]

    models = [
        ('ridge', lm.Ridge()),
        ('lasso', lm.Lasso()),
        ('elastic', lm.ElasticNet()),
        ('LassoLars', lm.LassoLars()),
        ('SGDRegressor', lm.SGDRegressor()),
        ('knn', KNeighborsRegressor(n_jobs = -1)),
    ]

    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],
        },
        'SGDRegressor': {
            'penalty': ['l1', 'l2'],
            'alpha': [0.001, 0.01, 0.1, 1.0, 10, 100],
            'fit_intercept': [True, False],
        },
        'knn': {
            "n_neighbors": range(2,7),
        }
    }
    
    #target 별 상관성이 상대적으로 낮은 변수들 제외하기 위한 x_colname 정의
    hour_bef_ozone_x_colname = [col for col in X_train_scaled.columns if col != 'hour_bef_precipitation' and col != 'hour_bef_ozone' and col != 'hour_bef_pm10' and col != 'hour_bef_pm2.5']
    hour_bef_pm10_x_colname =['hour_bef_humidity','hour_bef_visibility']
    hour_bef_pm25_x_colname = ['hour_bef_humidity','hour_bef_visibility','hour_bef_pm10'] 

    if target =="hour_bef_ozone":
      x_colname = hour_bef_ozone_x_colname
    elif target =="hour_bef_pm10":
      x_colname = hour_bef_pm10_x_colname
    else:
      x_colname = hour_bef_pm25_x_colname

    best_model, best_score = None, float('inf')
    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_colname], Y_train)

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

        evaluation = evaluate(Y_test, predictions)
        score = evaluation['mse'][0]
        print(model_name, score)

        if score < best_score:
            best_score=score
            best_model = model

    print("최적의 모델:",best_model)
    print("최종 mse score:",best_score,"\n")
    best_model = grid.best_estimator_
    best_train_pred = best_model.predict(ozone_isna_train_df[x_colname])
    best_test_pred = best_model.predict(ozone_isna_test_df[x_colname])

    # 예측값을 train,test에 적용
    ozone_nan_train_index = train[train[target].isna()][target].index
    ozone_nan_test_index = test[test[target].isna()][target].index

    for pred,index in zip(best_train_pred,ozone_nan_train_index):
      train[target].fillna({index:pred}, inplace=True)
    for pred,index in zip(best_test_pred,ozone_nan_test_index):
      test[target].fillna({index:pred}, inplace=True)

targets =['hour_bef_ozone', 'hour_bef_pm10', 'hour_bef_pm2.5']
Nan_prediction(targets)

 

ridge 0.00022735981284121033
lasso 0.0003577431018933861
elastic 0.00032536467486597313
LassoLars 0.000357741115357743
SGDRegressor 0.0019727722132471728
knn 0.0002798427571351642
최적의 모델: Ridge(alpha=0.01, copy_X=True, fit_intercept=True, max_iter=None,
      normalize=True, random_state=None, solver='auto', tol=0.001)
최종 mse score: 0.00022735981284121033 

ridge 479.574083827637
lasso 480.9698920519781
elastic 480.99426051508937
LassoLars 480.97117933041903
SGDRegressor 3.519192783192444e+29
knn 514.6375284552845
최적의 모델: Ridge(alpha=0.01, copy_X=True, fit_intercept=True, max_iter=None,
      normalize=True, random_state=None, solver='auto', tol=0.001)
최종 mse score: 479.574083827637 

ridge 84.67710204218932
lasso 84.72469340584847
elastic 84.63747722153994
LassoLars 84.7245776238065
SGDRegressor 1.022157379699085e+30
knn 45.39878500823723
최적의 모델: KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',
                    metric_params=None, n_jobs=-1, n_neighbors=4, p=2,
                    weights='uniform')
최종 mse score: 45.39878500823723

각 target별 연관성이 있다고 판단한 변수들만 뽑아서 예측해 보았습니다. ozone변수를 보면 여러개의 X feature들과 연관성을 가지고 있습니다. 반면 pm10, pm 2.5는 영향력있는 변수가 적었기 때문에 예측성능이 좋지 못했습니다. 따라서 

ozone은 예측값을 사용하고 그외 pm10, pm 2.5은 예측값을 넣되 사용하지 하지않기로 결정했습니다. 왜냐하면 이 둘의 결측값의 수만 해도 100개 이상이며 최종 target인 count와 연관성이 적기 때문입니다. (아래 이미지를 참고해주세요)

 

 

 

* 다음 시간 예고

다음 시간부터는 count의 출퇴근 수요를 설명해줄 변수를 생성하려고 합니다. 

아래는 X feature들의 분포입니다.

colum=['hour_bef_temperature','hour_bef_windspeed','hour_bef_humidity','hour_bef_ozone','hour_bef_pm10','hour_bef_pm2.5']
for col in colum:
  train.groupby(by='hour').mean()[col].plot(label=col)
  plt.legend()
  plt.show()

 

 

*Count 분포

train.groupby(by='hour').mean()['count'].plot()

출퇴근 시간에 수요가 급등하는 모습을 보이고 있습니다. 이러한 특징적인 현상을 설명해줄 변수가 필요합니다. X feature들의 분포를 보면 몇몇 변수에서 퇴근시간 증가하는 모습을 보입니다. 그렇지만 증가하는 분포? 폭이 넓기 때문에 기대이상으로 퇴근 시간대의 수요를 설명해 주기란 어려울 것으로 생각했습니다. 특히 출근 시간대의 수요증가는 hour뿐 이기 때문에 새로운 변수의 필요성을 느끼게 됩니다. 아직은 이렇게 해서 만들어야 겠다! 라는 아이디어는 떠오르지 않아서 시간이 좀 걸리지 않을까 생각해 봅니다 

 

좀 더 개선된 내용을 가지고 다음 시간에 포스팅 하겠습니다. 감사합니다

 

728x90
728x90

*오늘 얻어낸 것들

1. target의 log화는 성능을 떨어뜨린다. 

2. 오늘 시도했던 잡다한 방법들 모두 성능에 영향을 주지 못하거나 떨어뜨린다 (자잘한게 많아서 뭘 했는지 조차 기억..)

3. 결측값을 평균값으로 일괄 적용X

*현재 시도 중인 것

본래 결측치 값을 평균으로 싹 다 처리했습니다. 그런데 생각해 보니까 날씨 데이터가 있는데 평균값으로 대체 하면 말이 안되더라구요. 예를들어 비가 오는데 기온이 높으면 이치에 어긋나기 때문에 이를 수정하기로 했습니다.

 

 

특히, 마지막 3개는 결측값이 엄청 납니다. 가뜩이나 데이터도 많진 않아서 삭제하기 부담스럽습니다. 그래서 평균으로 대체 하는 것보다 예측값을 넣는게 차라리 낫지 않을까 하는 생각에 뒤에 이 3개 변수를 각각 예측모델링 짜고 있습니다.

전처리 하고 이것저것 자잘하게 처리 하고 그리드 서치 한 결과 test 예측부분에서

릿지모델에서 가장 낮은 mse를 기록했습니다. 해당 모델 가지고 적용하려는 순간 

 

best_model = grid.best_estimator_
best_pred = best_model.predict(test_ozone_scaled[x_colname])
ValueError: Input contains NaN, infinity or a value too large for dtype('float64').

 

test_ozone_scaled.isna().sum()
hour                      0
hour_bef_temperature      2
hour_bef_precipitation    2
hour_bef_windspeed        3
hour_bef_humidity         2
hour_bef_visibility       2

 

결측값이 있었던 걸 잊고 있었습니다. 해당 결측값들은 train할때 정확한 정보를 주고자 삭제를 시켰었습니다. 하지만 원본 데이터에는 그대로 존재하기 때문에 이 결측값을 먼저 처리해야 합니다. 결측값이 각 변수 별로 얼마 되지 않기 때문에 기존에 만들어 놓은 함수로 평균값 채우기 할 생각입니다. 오늘이 여기까지 진도 나갔습니다. 더 개선된 내용으로 업로드 하겠습니다. 

728x90
728x90

저번 포스팅까지는 X 변수가 하나라는 가정하에 진행해 왔습니다. 이제 부터는 X가 둘 이상인 multi을 다룹니다.

위 그림처럼, X들이 여러개 나뉘어 졌다. X1: 공부시간, X2: 등하교 시간으로 예를 들수 있겠습니다. 그래서 각각의 변수 데이터를 가지고 해당되는 target 값인 y를 예측하게 됩니다.

 

 

X2가 추가가 되었음을 볼 수 있다. 각 theta, X에 대해서 벡터 형식으로 정리가 가능하다. X 벡터의 1인 dummy 변수를 넣어 줘야 dot product 했을때 계산이 가능합니다.

 

 

Loss function에 th가 3개로 확장 되었으므로 더이상 평면이 아닌 공간 상에서 표현이 될 것입니다.

 

 

전에 배웠던 내용과 다른 점이 있다면 th2가 추가 되었다는 점 뿐이다.

 

 

각 th별로 미분을 하면 빨간색과 같은 수식이 나온다. 다만 달라진 점은 X인데 해당 X에 따라서 데이터 어떤 영향을 끼치는지 계속 자각해야 한다.

 

 

gradient을 구한뒤 GDM의 수식을 위 처럼 정리 할수 있겠다. th2은 X2에 영향을 받고, th1은 X1에 영향을 받는다는 점이 핵심이다.

 

 

Cost도 같은 과정을 거치게 되면 최종적으로 위 그림처럼 정리할 수 있다.

 

 

 

이번엔 X가 m개일 경우를 살펴본다. 예를 들어 100*100 이미지가 있다면 10000개의 X가 존재하고 이를 벡터형식으로 바꾼 모습이 위와 같을 것이다.

 

 

 

각각의 th,X의 벡터을 dot product을 시키면 맨 아래 수식처럼 간단한 형식으로 표현이 가능하다. Loss도 마찬가지로 같은 원리를 통해 간단하게 표한가능하다.

 

 

 

weight M개, bias 1개를 포함한 M+1개를 각각 gradients 구하면 위처럼 정리 할수 있다. m+1 차원의 Gradients가 만들어 진다. weight에는 P라는 첨자가 붙고 bias에는 붙지 않는다는 것에 대한 의미를 까먹지 않도록 상기 시켜야 한다.

 

 

 

 

Affine function 부분에서 forward, backward propagation이 발생하 넘겨주고, 받는 역할을 하게 된다. 누구에게 넘겨주느냐? Activation Funtion에게 넘겨주게 된다. 이런 전체적인 과정이 딥러닝이 일어나는 과정이다.

왼쪽 그림을 보면 둘로 나눠져 있다. 즉,affine funtion 뒤에 Activation이 붙든, Cross entropy가 붙든, soft max가 붙든, Loss 구하든지, cost를 구하든지 일절 상관이 없다. Affine은 단지 Z값을 전달하고, 자기가 내줬던 값에 대한 편미분 값을 받아서 업데이트 하는 것 뿐이다.

 

 

 

지금까지 배웠던 것을 그림으로 표현하면 위와 같다. Z값을 받고 Loss 혹은 Cost를 구한뒤 편미분을 통해 업데이트가 일어난다. 뉴런 1개에 이와 같은 연산이 발생한다.

multi linear에서는 Loss function이 어떻게 변하는지 알아보자.

 

 

 

Loss funtion

-편의상 두개의 x값을 가지고 진행한다.

각각의 datset에 따른 prediction과 Loss는 위 처럼 구할수 있을 것이다. 이것을 다음에 적용해 보도록 한다.

 

 

 

해당 식과 data가 있을때, 변화를 면밀하게 살펴보기 위해서 세개의 값중 하나를 고정시켜 보도록 한다. 가장 왼쪽은 bias가 학습이 다 됐다는 가정 하에 th의 변화를, 두번째는 th1이 학습완료가 됐다는 가정하에 th2와 th0간에 변화를 나타내고 마지막도 같은 원리로 진행한다.

 

 

 

 

가장 왼쪽 사진에서 적용된 data는 (1,1)이다. 파란색 식을 보면 (1,1)일때 비율이 같기 때문에(학습량이 같다) a=-x방향의 Loss funtion을 볼수 있다.

두번째 사진에서 적용된 data는 x1의 1뿐이다. 그러면 th2와 th0 간의 비율도 역시 같기 때문에 대각선 방향이며 세번째 그림도 같은 원리로 대각선 방향이다.

 

 

 

첫번째 사진에서 적용된 data는 (2,1)이다. th2가 더 높은 비율을 가지므로 th1보다 빨리 학습 될것이다. 그림에서 보듯이 기울기가 y축 방향으로 붙는걸 확인할수있다. th2방향으로 먼저 projection이 된다.

두번째 그림에서 적용된 값은 x2의 2이다. 파란색 식을 보면 2*a*x2 VS 2*a 이므로 2배차이가 발생한다. 그래서 첫번째 그림처럼 y축에 붙게 된다.

세번째 그림은 th1과 th0은 서로 같은 비율이기 때문에 y = -x 방향의 대각선을 갖게된다.

 

 

 

파란색과 빨간색은 서로 같은 비율이기 때문에 겹쳐있다.

 

 

 

왼쪽그림에서 x2=3, x1=2 일때, 이 둘의 비율 차이가 1.5배이다. 1보다는 크고 2보다는 작은 모습으로 만들어진 것이다.

가운데 그림에서는 비율이 3배 차이가 나기 때문에 기울기가 더욱 기울어진 모습을 보인다.

오른쪽 그림은 비율의 차이가 2배이므로 1보다는 크고 3보다는 작은 모습으로 만들어진다.

 

 

 

왼쪽그림에서 x1=10, x2= 3 일때, x1이 더 크기 때문에 먼저 학습이 될 것이다. 전에 배웠던 X의 값이 1보다 작을때의 Loss function 형태와 같아진다.

두번째 그림에서는 x2와 bias의 비율 차이가 3배이기 때문에 이와 같이 만들어진다.

세번째 그림에서는 10배 만큼 비율의 차이가 있으므로 y축에 바짝 붙는 형태가 만들어진다.

 

 

 

 

표준정규분포를 따르는 데이터를 입력으로 넣었을 때는 th들이 균등하게 학습이 되는 특징을 가지고 있다. 위 모습이 가장 "이상적인 학습의 모습"이다.

 

 

x2=5로 늘리 다. 또한 Loss의 양을 보면 초반에 초반에만 급격하게 치솟아 올랐다. 이는 급격한 th2의 학습이 원인이다. x값이 크면 Loss의 양도 비례하게 된다. Loss가 크다는 건 학습해야 할 양이 많다는 건데 X값이 너무 커지면 lr로 더욱 작게 설정하여 발산하지 못하도록 해야한다.

 

 

 

 

x2가 1보다 작은 수 라면, th2만 혼자서 학습속도가 느려진 걸 확인 할수 있다. 하단의 그림을 보면 th1, th0이 먼저 학습할려고 위쪽으로 끌어 당기는 모습이 보인다. 그리고 th1,th0은 같은 학습속도 이므로 좌하단처럼 대각선 모양을 가진다.

 

 

 

극단적으로 0.1를 std로 설정하면 거의 0 근처 값들만 나오게 될 것이다. 그래서 하단 그림을 보면 거~의 학습이 되지 않는다. interation을 많이 주게 되면 결국은 목표값에 도달하겠지만 그만큼 시간과 자원이 소모되는 걸 감안 했을때는 비효율적이다. 또한 중간에 끊기게 되면 th1,th0은 학습이 된 상태될 것이고, th2은 학습이 미완성인 상태로 끝나게 되는 문제점도 있다.

이번엔 평균이 바뀔때 변하는 모습을 보도록 한다.

 

 

 

X2의 평균값 자체가 커지면서 th2의 지배적인 학습이 진행됨을 볼수있다. 가운데를 보면 th2가 자기쪽으로 당기기 때문에 th0에 대한 학습이 지그재그로 천천히 학습이 되는 문제점이 생긴다.

 

 

 

상하단 그림을 비교해 보자. mean값이 커질수록 먼저 학습이 되기 때문에 하단처럼 th2,1이 먼저 치고 올라 가는걸 볼수 있다. 반면 th0은 더욱 느려진다. 왜냐면 Loss가 th2,1에게 많이 뺐겼기 때문이다. 하단의 loss를 보면 평행을 이룰만큼 거의 존재하지 않는다.

 

 

 

 

하단을 보면 공통적으로 보이는 것은 th2,1 쪽으로 먼저 빠르게 학습이 된 후에야 th0가 지그재그로 학습 되고 있다.

 

 

 

x2 평균이 -5 음수이다. 이때 특이한 모양이 보이는데 바로 윗 그림을 보면 th0값이 뒤로 오히러 퇴보? 뒤로 당겨지는 모습을 보인다. 이유는 간단하다. -5의 Loss function 방향으로 projection이 되다 보니까 이런 현상이 발생한 것이다. 초기값이 만일 다른 곳에서 출발했다면 또 다른 모습을 보일 것이다.

728x90
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
728x90

이번 포스팅에서는 주로 weight 변화에 따라서 bias에 어떠한 영향을 미치는지를 중점적으로 살펴보도록 한다.

weight, bias가 3,3인 것으로 부터 mu, sigma의 변화에 따른 영향을 살펴본다.

 

*표준편차가 1이하 일 때

(0, std)를 따를 때, std가 0에 가까울수록 뽑히는 x의 값이 0에 가까운 값이 뽑히게 될 것이다. 예를 들어 x의 값이 0.1, 0.5처럼 1보다 작은 값들이 뽑히게 된다는 것이다. 저번 포스팅에서 배웠던 것처럼 x의 값이 1보다 작게 되면 weight가 학습되는 양 자체가 적어진다. 그리고 std =1 일때는 th1,th0이 균등하게 대각선 방향으로 학습이 된다. 위 왼쪽 그림의 보락색이 이에 해당 된다.

 

th1의 loss grdient 양은 -2*x*lr(y-pred)이 된다. 이때 입력되는 x 값이 소수점이라면 gradient의 양 자체가 감소하게 된다. 그래서 th1의 학습속도가 느려지게 된다. 즉, step by step으로 학습이 매번 이뤄질때마다 학습 되는 양이 적다는 얘기다.

 

왼쪽 그림의 보락색은 초반에 띄엄 띄엄 학습되는 걸 볼수 있다. 반면 파란색은 매 학습 step마다 촘촘히 학습되고 있다. 이는 곧 "학습되는 양" 자체가 적어서 업데이트도 적게 이뤄진다는 걸 알수 있다.

오른쪽 th0의 학습 그래프를 보면, 학습 되는 속도가 std 변화에 따라서 크게 영향을 끼치지 않는다. std가 작아지든 커지든 th0의 학습에는 영향을 주지 않는 것인가? th0의 gradient를 보면 -2*lr*(y-pred) , x가 빠져 있다. 즉 x에 영향을 받지 않는 다는 것이다. 왼쪽 그림은 th1이 혼자 느려질때의 th1,th0의 그래프이고, th0만 따로 떼어 보면 오른쪽 처럼 균일한 학습속도를 보이게 되는 것이다.

 

 

*표준편차가 1이상 일때

반대로 std가 1보다 큰 값이 들어오게 된다면, th1의 gradient 값이 th0보다 많아 지기 때문에 왼쪽 그래프 처럼 th1에 지배적인 학습이 먼저 이뤄진다. gradient가 많아지게 되면 발생하는 대표적인 문제점이 하나 있다. 바로 발산이다. 그리고 발산하기 직전에는 지그재그로 왔다갔다 하게 된다. 왼쪽 그림에서 std가 가장 높은 점들을 보면 지그재그로 왔다갔다 학습이 이뤄지고 있다. 지그잭로 학습 되기 때문에 당연히 전반적인 학습 속도가 느리게 된다.

 

오른쪽 그림의 th0은 마찬가지로 gradient에서 x가 영향을 끼치지 않으므로 gradient의 양 변동이 크지 않다. 오직 th1의 gradient가 커졌다 작아졌다 할뿐이다.

가장 이성적인 학습속도는 어떤 것인가?? 바로 th1,th0이 "동시에" 학습이 완료되는 것이다. 마치 std=1일때 처럼 말이다.

 

정리를 하자면(std의 변화에 따른 결론이다)

1. std의 값이 1이하 일 때

-> th1의 gradient 양이 "작아서" 학습 속도가 느려진다.

2. std의 값이 1 이상 일 때

-> th1의 gradient 양이 커져서 "지그재그로 학습되어" 속도가 느려진다.

 

 

*mean 변화량에 따른 학습 영향

mean값이 커질수록 loss function은 수직으로 세워지게 된다.(앞서 학습 내용과 겹치므로 자세한 내용 생략) 따라서 mean이 높을수록 더욱 지그재그로 학습이 많이 이뤄진다. mean 0일 때와 5일 때를 비교해보자. th0의 학습 속도에 차이가 난다.

 

왜일까? mean =5이기 때문에 지그재그로 더 많이 학습이 되기 때문이다. 쉽게 말하자면 빨리 갈 수 있는 길을 내버려두고 돌고 돌아서 가기 때문에 목표지점에 도달하는 시간이 더 길어지는 것이다.

그런데, 오른쪽 그림을 보면 아까와는 다른 그래프 모양을 가진다. 왜일까? "지그재그"때문이다. 지그재그 할 때는 증감을 반복하기 때문이다. th0 입장에서 왼쪽 하늘색을 보면 증감의 반복을 볼수있다. 이런 현상이 왼쪽 그래프 반영된 것이다.

이러한 현상때문에 bias은 mean에 대해서 아주 큰 영향을 받게 된다. 따라서 input값들을 평균 0으로 맞춰 주는게 중요하다. 0으로 맞추면 std가 크든 작든 좌우 대칭으로 dataset이 뽑히기 때문에 위와 같은 악영향을 줄일수 있다.

 

이해를 돕기 위해서 위 학습 내용을 벡터로 표현해본다. 단, 그래프 표현을 위해서 벡터의 크기는 1로 통일한다 (gradient의 학습 양을 벡터로 그대로 표현하면 그래프로 보기가 힘들기 때문이다)

 

 

*벡터로 표현한 학습

오른쪽 벡터의 중심점이 본래 가지고 있던 점이다. 즉, 출발점이다. 벡터가 가리키는 것이 학습의 방향성이다. 왼쪽의 학습 방향을 벡터로 표현한 것이 오른쪽이다.

 

 

 

*std를 높힌 경우

std를 높힌다는 것은 x의 절대값이 커진다는 것이고, losss function contour plot이 y축으로 기울어 진다는 것이다.

오른쪽 그림를 보면 양쪽 좌우에 벡터가 뭉쳐 있다. 반면 상대적으로 가운데 벡터는 적다. 왜그럴까? std가 커진다는건 x의 절대값이 커진다는 것이다. 예를들어 -3,-5,3,4 이러한 x가 반영된다. 1보다 큰 x값들은 y축으로 기울진 즉, 수직에 가까운 loss function을 갖게 된다. 이러한 loss function에 projection되는 방향으로 학습이 되기 때문에 벡터 좌우에 몰리게 되고, 가운데 분포는 적게 된다. 좌우로 쏠린 벡터들이 서로 상쇄가 되면서 학습된다. 즉, 평균이 0이고, std만 달라져도 서로 대칭되는 값들이 뽑히기 때문에 서로 상쇄가 되어서 학습에 큰 영향을 주지 않게 된다. 진짜 문제는 평균이 0이 아닐때 발생하게 되는것이다.

 

참고로 가운데 벡터는 x축과 평행에 가까운 loss function에 projection되는 방향이다. 즉, x의 값이 1보다 작은 값들이 나올 때 이러한 벡터 방향이 나온다.

 

*mean를 높인 경우

mean을 키우게 되면, 전반적인 x의 값들을 키운 것과 같다. mean을 키운 순간 대각선 방향으로의 th1,th0 학습을 하지 못하게 된다. 어느 한쪽으로 쏠리게 되는데, 이 경우는 양수에 치우친 x값들이 생기게 된다. 따라서 왼쪽 하단에 분포가 많이 생기게 된다.

 

또한 (1,1)이므로 0에 가까운 loss function도 많이 만들어질 것이다. 그래서 오른쪽 상단 벡터에도 분포가 많이 생기게 된다. 즉, 왼쪽 오른쪽 분포가 많으므로 지그재그 형태를 띠게 된다.

mean더 키우게 된다는 것은 y축과 더욱 평행한 loss function이 만들어진다는 것이며, 사실상 음수 loss function이 만들어지지 않는다고도 볼 수 있다.

지금까지는 데이터 하나를 가지고 loss function의 변화를 봤다면, 지금부터는 여러 개의 데이터 샘플을 사용한 cost의 변화를 보도록 하자.

가장 이상적인 모습을 보인 건 전체적인 데이터 특성을 제일 잘 나타낸 배치 사이즈 32이다. 배치 사이즈가 커지면 아웃라이어의 영향을 줄일 수 있다. 그래서 배치 2를 보면 다른 것에 비해 불규칙적으로 움직인 걸 볼 수 있다.

 

 

*std 변화에 따른 영향

std =5 일 때의 그래프이다. 배치가 2인 파란색이 확실히 변동이 있는 반면, 배치가 32인 빨간색은 안정적으로 학습됨을 볼 수 있다.

 

 

*mean 변화에 따른 영향

mean 자체가 커지면서 지그재그 파동이 많아짐을 볼 수 있다. cost라도 mean이 커지면 지그재그 현상이 없어지지 않는다.

 

 

* std 변화에 따라서 벡터의 모양을 비교해 보록 하자

배치가 2일 땐 데이터 샘플 하나를 이용했을 때와 별 차이점이 없다. 그래서 좌우로 지그재그로 학습이 되기 때문에 벡터에도 좌우에 많은 분포를 가진다.

배치가 커질수록 벡터의 얖옆이 줄어들면서 가운데에 분포를 하게 된다. 다시 말해서 좌우로 가는 벡터가 적어짐에 따라 지그재그 현상이 감소하게 된다.

 

 

* mean변화에 따라서 벡터의 모양을 비교해 보록 하자

mean의 변화에 따른 벡터의 모양을 보면 비슷함을 알 수 있다. 특히 좌우 벡터의 양이 std 변화에 따른 벡터처럼 줄어들지가 않는다. 왜일까?

 

mean 자체를 증가시켰기 때문에 std=1이든 2이든 어찌 됐든 x가 큰 값이 input으로 들어가게 된다. mean이 커지면 더 이상 표준 정규분포가 아니게 된다. 위 그래프의 학습 cost function 모양이 대략  \일 것이다.

 

아무리 32개를 뽑고 cost를 계산해봐도 \과 비슷한 cost function을 만들게 된다. 또한 x의 값이 커지면 발산의 우려가 있다.  그 전초기 증상이 바로 지그재그이다. 이를 막기 위해서 lr의 조정이 필요한 것이다.

728x90

+ Recent posts