전체 글
-
[PYTHON - 머신러닝_캐글_실습-02]범주형 데이터 이진분류★o2023.02.13
[PYTHON - 머신러닝_캐글_실습-02]향후 판매량 예측★데이터 다운캐스팅★memory_usage()★to_numeric()★가비지 컬렉션(garbage collection)★
1. 피처엔지니어링 1 : 피처명 한글화
sales_train = sales_train.rename(columns = {'date' : '날짜' ,
'date_block_num' : '월ID',
'shop_id' : '상점ID',
'item_id' : '상품ID',
'item_price' : '판매가',
'item_cnt_day' : '판매량'})
sales_train.head()
shops = shops.rename(columns = {'shop_name' : '상점명' ,
'shop_id' : '상점ID'})
shops.head()
items = items.rename(columns = {'item_name': '상품명',
'item_id' : '상품ID',
'item_category_id' : '상품분류ID'})
items.head()
item_categories = item_categories.rename(columns = {'item_category_name' : '상품분류명',
'item_category_id' : '상품분류ID'})
item_categories.head()
test = test.rename(columns = {'shop_id' : '상점ID',
'item_id' : '상품ID'})
test.head()
2. 피처엔지니어링 2 : 데이터 다운캐스팅
다운캐스팅(downcasting) : 작은 데이터 타입으로 변환하는 작업
==> ex) 금붕어는 금붕어용 어항에서 키우는것이 좋다. 돌고래용 수족관을 마련할 필요가 없다.
==> 주어진 데이터 크기에 맞는 타입을 사용하는 것이 좋다.
1> 피처별 메모리 사용량을 바이트 단위로 출력
sales_train.memory_usage()
start_mem = sales_train.memory_usage().sum() / 1024**2
start_mem
==> 바이트단위의 Memory 사용량을 MB 단위로 구한다.
==> 50.3973
for col in df.columns:
dtype_name = df[col].dtype.name
if dtype_name == 'object':
pass
elif dtype_name == 'bool':
df[col] = df[col].astype('int8')
elif dtype_name.startswith('int') or (df[col].round() == df[col]).all():
df[col] = pd.to_numeric(df[col] , downcast= 'integer')
else:
df[col] = pd.to_numeric(df[col] , downcast ='float')
==> 데이터타입이 object이면 그대로 두기
==> 데이터타입이 bool 형이면 , int8로 바꾸기
==> 데이터 타입이 int형으로 시작하거나 실수형 타입 중 소수점 첫째 자리에서 반올림한 수가 원래 수 와 같다면 , 정수형으로 바꾸기
def downcast(df, verbose=True):
start_mem = df.memory_usage().sum() / 1024**2
for col in df.columns:
dtype_name = df[col].dtype.name
if dtype_name == 'object':
pass
elif dtype_name == 'bool':
df[col] = df[col].astype('int8')
elif dtype_name.startswith('int') or (df[col].round() == df[col]).all():
df[col] = pd.to_numeric(df[col] , downcast= 'integer')
else:
df[col] = pd.to_numeric(df[col] , downcast ='float')
end_mem = df.memory_usage().sum() / 1024**2
if verbose:
print('{:.1f}% 압축됨'.format(100 * (start_mem - end_mem) / start_mem))
return df
==> 다운 캐스팅 후 몇 % 압축되었는지 출력
all_df = [sales_train , shops , items , item_categories ,test]
for df in all_df:
df= downcast(df)
3. 피처엔지니어링 3 : 데이터 조합 생성
==> Test Data의 피처는 ID 피처를 제외하면 상점ID, 상품ID 피처이다.
==> 우리가 예측해야하는 값은 각 상점의 상품별 월간 판매량이다.
==> 월, 상점 ,상품별 조합이 필요하다.
from itertools import product
import numpy as np
train = []
# 월 ID , 상점 ID , 상품 ID 조합 생성
for i in sales_train['월ID'].unique():
all_shop = sales_train.loc[sales_train['월ID'] == i, '상점ID'].unique()
all_item = sales_train.loc[sales_train['월ID'] == i , '상품ID'].unique()
train.append(np.array(list(product([i] , all_shop , all_item))))
idx_features = ['월ID' , '상점ID' , '상품ID'] # 기준 피처
# 리스트 타입인 train을 DataFrame으로 변환
train = pd.DataFrame(np.vstack(train) , columns= idx_features)
train
https://knowallworld.tistory.com/230
복원, 비복원 추출★SET활용하여 차집합,여집합 가능!★기초통계학-[Chapter04 - 경우의 수-03]
1. 복원추출(Replacement) ==> 표본공간에서 표본점을 선택할 때 , 동일한 표본점이 1번이상 반복하여 추출되도록 허용 EX) 1~5까지의 숫자가 적힌 공이 들어 있는 주머니에서 차례대로 2개의 공 꺼내기
knowallworld.tistory.com
==> 월ID 기준 , 상점ID , 상품ID 피처 조합을 생성
==> product 는 중복순열
ex)1~5까지의 숫자가 적힌 공이 들어 있는 주머니에서 차례대로 2개의 공 꺼내기 ==> 처음 꺼낸 공을 다시 주머니에 넣고 2번째 공을 꺼내기
4. 피처엔지니어링 4 : 타깃값(월간 판매량) 추가
idx_features = ['월ID' , '상점ID' , '상품ID'] # 기준 피처
# idx_features를 기준으로 그룹화해 판매량 합 구하기
group = sales_train.groupby(idx_features).agg({'판매량' : 'sum'})
# 인덱스 재설정
group = group.reset_index()
# 피처명을 '판매량'에서 '월간 판매량'으로 변경
group = group.rename(columns = {'판매량' : '월간 판매량'})
group
==> 월ID, 상점ID, 상품ID를 기준으로 그룹화 한 뒤 월별 판매량 더하기
# train과 group 병합하기
train = train.merge(group, on=idx_features , how = 'left')
train
==> train 과 group 병합하기
==> group에서 월ID, 상점ID , 상품ID로 조합을 생성했다.
==> 기존에 없던 조합에는 판매량 정보가 없는것이 당연하다.
==> 값이 없다는 건 판매량이 0이라는 뜻이므로 결측값은 0으로 대체한다.
5. 가비지 컬렉션(Garbage Collection)
==> 메모리 절약 방법
==> 할당한 메모리중 더는 사용하지 않는 영역을 해제하는 기능이다.
import gc # 가비지 컬렉터 불러오기
del group # 더는 사용하지 않는 변수 지정
gs.collect() # 가비지 컬렉션 수행
6. 피처 엔지니어링 5 : 테스트 데이터 이어붙이기
# train과 test 이어붙이기
all_data = pd.concat([train , test.drop('ID' , axis = 1)],
ignore_index= True, # 기존 인덱스 무시(0부터 새로 시작)
keys = idx_features) # 이어붙이는 기준이 되는 피처
==> test 데이터의 ID피처는 단순한 식별자로써, 인덱스로 충분하기 때문에 train에는 'ID 피처를 제거한' test를 이어붙여야 한다.
==> test 데이터에는 타깃값이 없기 때문에, 이어붙인 all_Data의 결측값은 0으로 대체한다.
all_data = all_data.fillna(0)
all_data
7. 피처 엔지니어링 6 : 나머지 데이터 이어붙이기
# 나머지 데이터 병합
all_data = all_data.merge(shops , on = '상점ID' , how = 'left')
all_data = all_data.merge(items , on = '상품ID' , how = 'left')
all_data = all_data.merge(item_categories , on ='상품분류ID' , how = 'left')
all_data
# 데이터 다운캐스팅
all_data = downcast(all_data)
# 가비지 컬렉션
del shops, items, item_categories
gc.collect()
==> 다운캐스팅 및 가비지 컬렉션 실행
all_data = all_data.drop(['상점명' , '상품명' , '상품분류명'] , axis =1 )
all_data.head()
==> 상점ID, 상품ID, 상품분류ID와 상점명,상품명,상품분류명과 매칭되므로 제거해도 된다.
8. 피처 엔지니어링 7 : 훈련데이터, 검증데이터, 테스트데이터 생성
훈련 데이터 : 2013년 1월 부터 2015년 9월(월ID = 32) 까지 판매내역
검증 데이터 : 2015년 10월(월ID=33) 판매내역
테스트 데이터 : 2015년 11월(월ID=34) 판매내역
# 훈련 데이터(피처)
X_train = all_data[all_data['월ID'] < 33]
X_train = X_train.drop(['월간 판매량'], axis =1)
# 검증 데이터(피처)
X_valid = all_data[all_data['월ID'] == 33]
X_valid = X_valid.drop(['월간 판매량'], axis = 1)
# 테스트 데이터(피처)
X_test = all_data[all_data['월ID'] == 34]
X_test = X_test.drop(['월간 판매량'], axis = 1)
# 훈련 데이터 (타깃값)
y_train = all_data[all_data['월ID']<33]['월간 판매량']
y_train = y_train.clip(0,20) # 타깃값을 0~20으로 제한 , 하한값과 상한값을 잘라준다
# 검증 데이터 (타깃값)
y_valid = all_data[all_data['월ID'] ==33]['월간 판매량']
y_valid = y_valid.clip(0,20)
==> y_train = y_train.clip(0,20) ==> 타깃값을 0~20으로 제한하여 하한값과 상한값을 잘라준다.
==> 클리핑은 이상치를 제거할때 유용하다.
9. 모델 훈련 및 성능 검증
==> 베이스라인 모델로 LightGBM을 사용한다. 기본 파라미터만 설정하고 LightGBM용 데이터셋을 만들어서 훈련한다.
==> categorical_feature 파라미터에는 범주형 데이터를 전달하면 된다.
==> 범주형 데이터로는 상점ID , 상품ID, 상품분류ID 가 있다.
==> 이중 상품ID를 뺀 상점ID와 상품분류ID만 인수로 전달한다.
==> 상품ID는 고윳값 개수가 상당히 많다. LightGBM은 고윳값 개수가 너무 많은 범주형 데이터는 수치형 데이터로 취급해야 성능이 더 좋다.
import lightgbm as lgb
# LightGBM 용 하이퍼파라미터
params = {'metric' : 'rmse', # 평가지표 = rmse
'num_leaves' : 255, # 개별 트리가 가질수 있는 최대 말단 노드 개수
'learning_rate' : 0.01, # 학습률(부스팅 이터레이션을 반복하면서 모델을 업데이트하는 데 사용 되는 비율)
'force_col_wise' : True, # 메모리 용량이 충분하지 않을 때 메모리 효율을 높이는 파라미터
'random_state' : 10 # 랜덤 시드값(코드를 반복 실행해도 같은 결과가 나오게 지정하는 값)
}
# 범주형 피처 설정
cat_features = ['상점ID' , '상품분류ID']
# LightGBM용 훈련 및 검증 데이터셋
dtrain = lgb.Dataset(X_train, y_train) # LightGBM 전용 훈련 데이터 셋
dvalid = lgb.Dataset(X_valid , y_valid) # LightGBM 전용 검증 데이터 셋
# LightGBM 모델 훈련
lgb_model = lgb.train(params = params, # 훈련용 데이터
train_set= dtrain, # 훈련 데이터 셋
num_boost_round= 500, # 부스팅 반복 횟수
valid_sets= (dtrain , dvalid), # 성능 평가용 검증 데이터 셋
categorical_feature= cat_features , # 범주형 데이터 파라미터, 이 파라미터에 전달된 데이터를 범주형 데이터로 인식한다.
verbose_eval= 50)# 50번째 마다 점수 출력
preds = lgb_model.predict(X_test).clip(0 ,20) # 테스트 데이터를 활용해 타깃값을 예측해보면 타깃값은 0~20 사이의 값이어야 하므로 예측한 값도 clip() 함수로 범위를 제한하였다.
# 제출 파일 생성
submission['item_cnt_month'] = preds
submission
'머신러닝 > 캐글_실습' 카테고리의 다른 글
[PYTHON - 머신러닝_캐글_실습-01]향후 판매량 예측★groupby★boxplot★merge
https://www.kaggle.com/competitions/competitive-data-science-predict-future-sales/data
Predict Future Sales | Kaggle
www.kaggle.com
1. 향후 판매량 예측
==> 2013년 1월 부터 2015년 10월까지 판매 데이터를 기반으로 2015년 11월 판매량 예측
독립변수(피처) : 상점 및 상품에 관한 정보
종속변수(타깃값) : 월간 판매량
==> 월간 판매량은 0~20개 사이여야 한다. ==> 판매량이 20개보다 많으면 20개로 간주한다는 뜻이다.
2. 탐색적 데이터 분석
1> sales_train 데이터
sales_train.head()
==> date 피처는 날짜를 의미
==> date_block_num 피처는 편의상 상용하는 날짜(월) 구분자, 0은 2013년 2월, 1은 2013년 3월
==> item_cnt_Day 피처는 당일 판매량을 나타낸다.
==> 각 상점의 상품별 일일 판매량을 월별로 합친 값이 곧 각 상점의 상품별 월간 판매량이다.
==> date_block_num 피처를 기준으로 그룹화해서 item_cnt_day 값을 합하면 타깃값(월간 판매량)이 된다.
sales_train.info(show_counts = True) # show_counts 는 비결측값 개수 표시
==> 메모리 사용량이 134.4MB
==> 제공한 데이터는 시계열 데이터이다.
==> 시계열 데이터는 시간 흐름이 중요하다.
==> 2013년 1월부터 2015년 9월까지 판매 내역을 훈련 데이터로 사용하고,
==> 2015년 10월 판매 내역을 검증 데이터로 사용한다.
==> 앞 장에서는 여러 폴드로 나눠 훈련 데이터와 검증 데이터를 지정했다.(oof 예측) 하지만 시계열 데이터는 이러면 과거와 미래가 섞이기 때문에 이용할 수 없다.
※ OOF(Out of Fold Prediction) 방식
==> K 폴드 교차 검증을 수행하면서 각 폴드마다 테스트 데이터로 예측하는 방식이다.
==> K 폴드 교차 검증을 하면서 폴드마다
1) 훈련 데이터로 모델을 훈련하고,
2)검증 데이터로 모델 성능을 측정하며 ,
3) 테스트 데이터로 최종 타깃 확률도 예측한다. 훈련된 모델로 마지막에 한 번만 예측하는 것이 아니다. 각 폴드별 모델로 여러번 예측해 평균을 내는 방식이다.
2> shops 데이터
==> shop_name 피처의 값들의 맨 앞 단어들이 도시 이름이다.
==> shop_id 의 경우 sales_train에도 있던 피처이므로 병합이 가능하다.
3> items 데이터
==> 상품명에서는 유용한 정보를 얻기 힘들어, 모델링 할 땐 제거한다.
==> item_id 피처는 sales_train 데이터에도 존재하는 피처이므로, item_id 피처를 기준으로 sales_train과 items를 병합할 수 있다.
4> item_categories 데이터
==> 상품분류명과 상품분류 ID로 구성되어 있다.
5> test 데이터
==> 테스트 데이터 식별자인 ID와 상점 ID , 상품ID로 구성돼 있다.
여기서 각 상점의 상품별 월간 판매량을 예측해야한다.
3. 데이터 병합
train = sales_train.merge(shops , on = 'shop_id' , how ='left')
train = train.merge(items , on = 'item_id' , how = 'left')
train = train.merge(item_categories , on = 'item_category_id' , how = 'left')
train
요약표
def resumetable(df):
print(f'데이터셋 형상 : {df.shape}')
summary = pd.DataFrame(df.dtypes , columns = ['데이터타입'])
summary = summary.reset_index()
summary = summary.rename(columns= {'index' : '피처'})
summary['결측값 개수'] = df.isnull().sum().values # 결측값의 개수 세기
summary['고윳값 개수'] = df.nunique().values # 고윳값의 개수 ㅅ기
summary['첫 번째 값'] = df.loc[0].values
summary['두 번째 값'] = df.loc[1].values
return summary
resumetable(train)
==> shop_id , shop_name ==> 개수 동일
==> item_category_id , item_category_name ==> 개수 동일
==> id와 name은 일대일로 매칭된다는 뜻이다.
4. 데이터 시각화
import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
sns.boxplot(y='item_cnt_day' , data = train)
# 이상치가 많아 모양이 납작해졌다. 과한 이상치는 제거해야 한다.
==> 이상치 범위가 과도하게 넓어서 납작해보인다.
1> 그룹화
# 그룹화는 회귀 문제에서 자주 쓰인다. groupby() 함수
group = train.groupby('date_block_num').agg({'item_cnt_day' : 'sum'})
# date_block_num 피처를 기준으로 그룹화해 item_cnt_day 피처의 합(sum)을 구하는 코드이다.
# 월별(date_block_num) 월간 판매량(item_cnt_day)의 합을 구한다는 말이다.
group.reset_index()
groupby 원리
1. DataFrame에 있는 한 개 이상의 피처를 기준으로 데이터를 분리한다.
2. 분리된 각 그룹에 함수를 적용해 집계값을 구한다.(agg() 메서드로 item_cnt_day 피처에 'sum'함수를 적용해 판매량 합계를 구한다.
3. 기준 피처별로 집계값 결과를 하나로 결합한다.
# sum , mean ,median ,std ,var , count , min , max
mpl.rc('font' , size = 13)
figure , ax = plt.subplots()
figure.set_size_inches(11,5)
# 월별 총 상품 판매량
group_month_sum = train.groupby('date_block_num').agg({'item_cnt_day' : 'sum'})
group_month_sum = group_month_sum.reset_index()
# 월별 총 상품 판매량 막대 그래프
sns.barplot(x = 'date_block_num' , y ='item_cnt_day' , data = group_month_sum)
# 그래프 제목 , x축 라벨, y축 라벨명 설정
ax.set(title = 'Distribution of monthly item counts by date block number' ,
xlabel = 'Date block number' ,
ylabel = 'Monthly item counts')
figure, ax = plt.subplots()
figure.set_size_inches(11,5)
# 상품분류별 총 상품 판매량
group_cat_sum = train.groupby('item_category_id').agg({'item_cnt_day' : 'sum'})
group_cat_sum = group_cat_sum.reset_index()
# 월간 판매량이 10,000개를 초과하는 상품분류만 추출
group_cat_sum = group_cat_sum[group_cat_sum['item_cnt_day'] > 10000]
# 상품분류별 총 상품 판매량 막대 그래프
sns.barplot(x = 'item_category_id' , y = 'item_cnt_day' , data = group_cat_sum)
ax.set(title = 'Distribution of total item counts by item category id' ,
xlabel = 'Date block number',
ylabel = 'Total item counts')
ax.tick_params(axis = 'x' , labelrotation = 90) # x축 라벨 회전
figure , ax = plt.subplots()
figure.set_size_inches(11,5)
# 상점별 총 상품 판매량
group_shop_sum = train.groupby('shop_id').agg({'item_cnt_day' : 'sum'})
group_shop_sum = group_shop_sum.reset_index()
group_shop_sum = group_shop_sum[group_shop_sum['item_cnt_day'] > 10000]
# 상점별 총 상품 판매량 막대 그래프
sns.barplot(x = 'shop_id' , y= 'item_cnt_day' , data = group_shop_sum)
ax.set(title = 'Distribution of total item counts by shop id',
xlabel = 'Date block number',
ylabel = 'Total item counts')
ax.tick_params(axis = 'x' , labelrotation =90)
'머신러닝 > 캐글_실습' 카테고리의 다른 글
[PYTHON - 머신러닝_캐글_실습-04]안전 운전자 예측 모델링★XGBoost★LightGBM과 XGBoost 앙상블
1. 성능 개선 ll : XGBoost 모델
XGBoost: 성능이 우수한 트리 기반 부스팅 알고리즘. 결정 트리를 병렬로 배치하는 랜덤 포레스트와 달리 직렬로 배치한다.
https://knowallworld.tistory.com/389
[PYTHON - 머신러닝_캐글_모델]★XGBoost★LightGBM
1. XGBoost(Extreme gradient boosting) ==> 성능이 우수한 트리 기반 부스팅 알고리즘 https://knowallworld.tistory.com/377 [PYTHON - 머신러닝_XGBoost]★pd.options.display.max_columns★정밀도, 재현율, F1-score 1. 부스팅 알고리
knowallworld.tistory.com
2. 피처 엔지니어링
# LightGBM 용 gini() 함수
def gini(preds , dtrain):
labels = dtrain.get_label()
return 'gini' , eval_gini(labels , preds) , True
==> LightGBM 용 평가지표(지니계수)는 반환값이 3개로, 평가지표명 , 평가점수, 평가점수가 높으면 좋은지 여부
# XGBoost용 gini() 함수
def gini(preds , dtrain):
labels = dtrain.get_label()
return 'gini' , eval_gini(labels,preds)
==> XGBoost 용 지니계수 계산 함수는 반환값이 2개 이다. 평가지표명과 평가점수만 반환한다.
==> 평가점수가 높으면 좋은지 여부는 XGBoost 모델 객체의 train() 메서드에 따로 전달해야한다.
3. 하이퍼파라미터 최적화
1> 데이터셋 준비
import xgboost as xgb
from sklearn.model_selection import train_test_split
# 8:2 비율로 훈련 데이터 , 검증 데이터 분리( 베이지안 최적화 수행용)
X_train , X_valid , y_train , y_valid = train_test_split(X,y, test_size=0.2 , random_state=0)
# 베이지안 최적화용 데이터셋
bayes_dtrain = xgb.DMatrix(X_train , y_train)
bayes_dvalid = xgb.DMatrix(X_valid, y_valid)
2> 하이퍼파라미터 범위 설정
# 베이지안 최적화를 위한 하이퍼파라미터 범위
param_bounds = {'max_depth' : (4 , 8) , # 개별 트리의 최대 깊이, 트리 깊이가 깊을수록 모델이 복잡해지고 과대적합 우려
# 값이 클수록 깊이가 한 단계만 늘어나도 메모리 사용량이 급격히 많아진다.
# 일반적으로 3~10 사이의 값을 주로 사용한다.
'subsample' : (0.6 , 0.9), # 개별 트리를 훈련할 때 사용할 데이터 샘플링 비율
# 0~1 사이 값으로 설정할 수 있다.
# 0.5 로 설정하면 전체 데이터의 50%를 사용해 트리를 생성
'colsample_bytree' : (0.7 , 1.0), # 개별 트리를 훈련할 때 사용하는 피처 샘플링 비율
# subsample 과 유사한 개념, subsample은 전체 데이터에서 얼마나 샘플링할지 나타내는 비율
# colsample_bytree는 전체 피처에서 얼마나 샘플링할지 나타내는 비율
# 값이 작을수록 과대적합 방지 효과
'min_child_weight' : (5 , 7), # 과대적합 방지위한 값, 값이 클수록 과대적합 방지 효과가 있다.
'gamma' : (8 , 11), # 말단 노드가 분할하기 위한 최소 손실 감소 값
# 소실 감소가 gamma보다 크면 말단 노드를 분할
# 값이 클수록 과대적합 방지 효과가 있다.
'reg_alpha' : (7 , 9) , # L1 규제 조정 값 , 값이 클수록 과대적합 방지 효과
'reg_lambda' : (1.1 , 1.5), # L2 규제 조정값 , 값이 클수록 과대적합 방지 효과
'scale_pos_weight' : (1.4 , 1.6)} # 뷸균형 데이터 가중치 조정 값 ,
# 타깃값이 불균형할 때 양성 값에 scale_pos_weight 만큼 가중치를 줘서 균형을 맞춤(타깃값 1을 양성 값으로 간주)
# 일반적으로 scale_pos_weight 값을 (음성 타깃값 개수 / 양성 타깃값 개수) 로 설정
# 값이 고정된 하이퍼파라미터
fixed_params = {'objective' : 'binary:logistic' ,# 훈련 목적 , binary : logistic( 확률값을 구하는 이진분류)
# reg : squarederror (회귀 문제)
# 소프트맥스 함수를 사용하는 다중분류에서는 multi : softmax 사용
# 확률값을 구하는 다중분류에서는 'multi : softprob' 사용
'learning_rate' : 0.02, # 학습률( 부스팅 스텝을 반복하면서 모델을 업데이트하는 데 사용되는 비율)
'random_state' : 1991} # 랜덤 시드값(코드를 반복 실행해도 같은 결과가 나오게 지정하는 값)
==> 이진분류 문제이므로 objective 는 binary : logistic으로 설정했다.
==> learning_rate 와 random_state도 고정했다.
3> 평가지표 계산 함수 작성
def eval_function(max_depth , subsample , colsample_bytree , min_child_weight , reg_alpha , gamma , reg_lambda , scale_pos_weight) :
# 최적화하려는 평가지표(지니계수) 계산 함수
# 베이지안 최적화를 수행할 하이퍼파라미터
params = {'max_depth' : int(round(max_depth)) , # 개별 트리의 최대깊이
'subsample' : subsample, # 개별 트리를 훈련할 때 사용할 데이터 샘플링 비율
'colsample_bytree' : colsample_bytree , # 개별 트리를 훈련할때 사용하는 피처 샘플링
'min_child_weight' : # 과대적합 방지위한 값
min_child_weight,
'gamma' : gamma, # 말단 노드가 분할하기 위한 최소 손실 감소 값
'reg_alpha' : reg_alpha, # L1 규제 조정값
'reg_lambda' : reg_lambda, # L2 규제 조정값
'scale_pos_weight' : scale_pos_weight} # 불균형 데이터 가중치 조정값
# 값이 고정된 하이퍼파라미터도 추가
params.update(fixed_params)
print('하이퍼파라미터 : ' , params)
# XGBoost 모델 훈련
xgb_model = xgb.train(params = params ,
dtrain = bayes_dtrain,
num_boost_round= 2000,
evals = [(bayes_dvalid , ' bayes_dvalid')],
maximize = True,
feval = gini,
early_stopping_rounds= 200,
verbose_eval= False)
best_iter = xgb_model.best_iteration # 최적 반복횟수
# 검증 데이터로 예측 수행
preds = xgb_model.predict(bayes_dvalid , iteration_range=(0, best_iter))
# 지니계수 계산
gini_score = eval_gini(y_valid, preds)
print(f'지니계수 : {gini_score}\n')
return gini_score
==> eval_function() 함수는 XGBoost 하이퍼파라미터를 인수로 받아서 XGBoost를 훈련한 뒤 평가지표인 지니계수를 반환한다.
# XGBoost 모델 훈련 , train() 메서드의 하이퍼파라미터
xgb_model = xgb.train(params = params , # XGBoost 모델의 하이퍼파라미터 목록 , 딕셔너리 타입으로 전달
dtrain = bayes_dtrain, # 훈련 데이터셋, xgboost.DMatrix 타입으로 전달
num_boost_round= 2000, # 부스팅 반복 횟수, 정수형 타입으로 전달
# num_boost_round 값이 클수록 성능이 좋아질 수 있으나 과대적합의 우려가 있다.
# num_boost_round 값이 작으면 반복 횟수가 줄어들어 훈련 시간이 짧아진다.
# 일반적으로 num_boost_round를 늘리면 learning_rate를 줄여야 한다.
evals = [(bayes_dvalid , ' bayes_dvalid')],
# 모델 성능 평가용 검증 데이터셋
# (DMatrix, 문자열) 쌍들을 원소로 갖는 리스트 타입으로 전달, 검증 데이터셋 이름을 원하는 대로 문자열로 정하면 된다.
maximize = True, # feval 평가지수가 높으면 좋은지 여부
feval = gini, # 검증용 평가지표, 사용자 정의 함수 형태
# evals를 활용해 모델 성능을 검증할 때 사용할 사용자 정의 평가지표 함수
# 예측값과 실제값을 파라미터로 전달받아, 평가지표명과 평가점수를 반환하는 함수이다.
early_stopping_rounds= 200,
# 조기종료 조건
# 모델은 기본적으로 num_boost_round만큼 훈련을 반복하며, 매 이터레이션마다 evals로 모델 성능을 평가하여 성능이 연속으로
# 좋아지지 않는다면 훈련을 중단하는데, 훈련 중단에 필요한 최소횟수가 early_stopping_rounds 이다. 즉 , early_stopping_rounds
# 동안 모델 성능이 좋아지지 않는다면 훈련을 중단한다.
# 과대적합 방지 효과
# 조기종료를 적용하기 위해서는 evals 에 검증 데이터가 하나 이상 있어야한다. 또한 evals에 검증 데이터가 여러 개라면 마지막 검증
# 데이터를 기준으로 조기종료 조건을 적용한다.
verbose_eval= False) # 성능 점수 로그 설정 값
# True 로 설정하면 매 부스팅 스텝마다 평가점수르 출력
# 출력값이 너무 많아지는 것을 방지하기위해 verbose_eval로 설정
==> xgb 모델 훈련
==> LightGBM 은 기본적으로 훈련 단계에서 성능이 가장 좋았던 반복 횟수 때의 모델을 활용해 예측한다.
best_iter = xgb_model.best_iteration # 최적 반복횟수
==> XGBoost는 성능이 가장 좋을 때의 부스팅 반복 횟수를 예측시 iteration_range 파라미터로 명시해줘야 최적 반복 횟수로 훈련된 모델을 활용해 예측한다.
xgb_model = xgb.train(params = params , # XGBoost 모델의 하이퍼파라미터 목록 , 딕셔너리 타입으로 전달
dtrain = bayes_dtrain, # 훈련 데이터셋, xgboost.DMatrix 타입으로 전달
num_boost_round= 2000, # 부스팅 반복 횟수, 정수형 타입으로 전달
# num_boost_round 값이 클수록 성능이 좋아질 수 있으나 과대적합의 우려가 있다.
# num_boost_round 값이 작으면 반복 횟수가 줄어들어 훈련 시간이 짧아진다.
# 일반적으로 num_boost_round를 늘리면 learning_rate를 줄여야 한다.
evals = [(bayes_dvalid , ' bayes_dvalid')],
# 모델 성능 평가용 검증 데이터셋
# (DMatrix, 문자열) 쌍들을 원소로 갖는 리스트 타입으로 전달, 검증 데이터셋 이름을 원하는 대로 문자열로 정하면 된다.
maximize = True, # feval 평가지수가 높으면 좋은지 여부
==> evals는 검증 데이터를 전달받는 파라미터로, 검증 데이터와 검증 데이터 이름의 쌍을 튜플로 묶어서 evals = [(bayes_dvalid , 'bayes_dvalid')] 형태로 전달했다.
==> 검증용으로 훈련 데이터와 검증 데이터 모두 사용하고 싶다면 [(bayes_dtrain, 'bayes_dtrain') , (bayes_dvalid , 'bayes_dvalid')] 처럼 전달해도 된다.
==> maximize 파라미터에는 True를 전달하여, 평가점수(지니계수)가 클 수록 좋다는 것을 의미시킨다
# 검증 데이터로 예측 수행
preds = xgb_model.predict(bayes_dvalid , iteration_range=(0, best_iter))
==> LightGBM 예측 코드는 lgb_model.predict(X_valid) 이다. Dataset 타입(LightGBM 용 데이터셋 타입) 이 아니라 원본 데이터 타입인 X_valid를 그대로 전달하였다.
==> XGBoost의 predict()에는 데이터를 DMatrix 타입(XGBoost용 데이터셋 타입)으로 전달해야한다.
4> 최적화 수행
from bayes_opt import BayesianOptimization
# 베이지안 최적화 객체 생성
optimizer = BayesianOptimization(f= eval_function, pbounds = param_bounds , random_state= 0)
# 베이지안 최적화 수행
optimizer.maximize(init_points= 3 , n_iter= 6)
# 평가함수 점수가 최대일 때 하이퍼파라미터
max_params = optimizer.max['params']
max_params
# 정수형 하이퍼파라미터 변환
max_params['max_depth'] = int(round(max_params['max_depth']))
# 값이 고정된 하이퍼파라미터 추가
max_params.update(fixed_params)
max_params
4. 모델 훈련 및 성능 검증
from sklearn.model_selection import StratifiedKFold
# 층화 K 폴드 교차 검증기 생성
folds = StratifiedKFold(n_splits= 5 , shuffle= True , random_state= 1991)
# OOF 방식으로 훈련된 모델로 검증 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_val_preds = np.zeros(X.shape[0])
# OOF 방식으로 훈련된 모델 훈련 , 검증 , 예측
for idx , (train_idx , valid_idx) in enumerate(folds.split(X,y)):
# 각 폴드를 구분하는 문구 출력
print('#' *40, f'폴드 {idx+1} / 폴드 {folds.n_splits}' , '#'*40)
# 훈련용 데이터, 검증용 데이터 설정
X_train , y_train = X[train_idx] , y[train_idx]
X_valid , y_valid = X[valid_idx] , y[valid_idx]
#XGBoost 전용 데이터셋 생성
dtrain = xgb.DMatrix(X_train , y_train)
dvalid = xgb.DMatrix(X_valid , y_valid)
dtest = xgb.DMatrix(X_test)
#XGBoost 모델 훈련
xgb_model = xgb.train(params = max_params,
dtrain = dtrain,
num_boost_round = 2000,
evals = [(dvalid , 'valid')],
maximize = True,
feval = gini,
early_stopping_rounds = 200,
verbose_eval = 100)
# 모델 성능이 가장 좋을 때의 부스팅 반복 횟수 저장
best_iter= xgb_model.best_iteration
# 테스트 데이터를 활용해 OOF 예측
oof_test_preds += xgb_model.predict(dtest, iteration_range = (0 , best_iter))/ folds.n_splits
oof_test_preds_xgb = oof_test_preds
# 모델 성능 평가를 위한 검증 데이터 타깃값 예측
oof_val_preds[valid_idx] += xgb_model.predict(dvalid , iteration_range=(0, best_iter))
# 검증 데이터 예측 확률에 대한 정규화 지니계수
gini_score = eval_gini(y_valid , oof_val_preds[valid_idx])
print(f'폴드 {idx+1} 지니계수 : {gini_score}\n')
==> 조기종료 조건(early_stopping_rounds)이 200회 이므로 1799에서 가장 좋은 성능을 보였다.
==> 1799에서 지니계수가 0.296401275 였다는 것이다.
print('OOF 검증 데이터 지니계수 : ' , eval_gini(y , oof_val_preds))
OOF 검증 데이터 지니계수 : 0.28905038063620453
==> 폴드 5개의 지니계수의 평균값??
https://knowallworld.tistory.com/397
[PYTHON - 머신러닝_캐글_실습-02]안전 운전자 예측 모델링★베이지안 최적화★LightGBM★K-폴드 교차
1. 베이스라인 모델 1) 데이터 불러오기 2) 피처 엔지니어링 --> 명목형 피처 원-핫 인코딩 --> 필요 없는 피처 제거 3) 평가지표 계산 함수 작성 --> 정규화 지니계수 4) 모델 훈련 --> 모델 : LightGBM -->
knowallworld.tistory.com
5. 성능 개선 lll : XGBoost 모델과 LightGBM 앙상블
==> 두 모델의 예측값을 결합하면 더 좋은 점수를 얻을 수 잇다. 여러 모델에서 얻은 예측 결과를 결합해 더 좋은 예측값을 도출하는 방식을 앙상블 이라고 한다.
1> 앙상블 수행
==> LightGBM으로 예측한 확률값을 oof_test_preds_lgb라 하고, XGBoost로 예측한 확률값을 oof_test_preds_xgb라고 한다.
oof_test_preds = oof_test_preds_lgb *0.5 + oof_test_preds_xgb * 0.5
submission['target'] = oof_test_preds
==> 각각에 공평하게 50% 씩의 가중치를 부여하여 구한 가중평균을 최종 예측 확률로 정한다.
submission
'머신러닝 > 캐글_실습' 카테고리의 다른 글
[PYTHON - 머신러닝_캐글_실습-03]안전 운전자 예측 모델링★LightGBM 모델★최적의하이퍼파라미터찾기★베이지안 최적화
7. 성능개선 1 : LightGBM 모델
def eval_function(num_leaves , lambda_l1 , lambda_l2 , feature_fraction , bagging_fraction , min_child_samples , min_child_weight) :
# 최적화하려는 평가지표(지니계수) 계산 함수
# 베이지안 최적화를 수행할 하이퍼파라미터
params = {'num_leaves' : int(round(num_leaves)) , # 개발 트리가 가질 수 있는 최대 말단 노드 개수, 트리 복잡도 결정 , 값이 클수록 좋다.
'lambda_l1' : lambda_l1, # L1 규제 조정값 , 값이 클 수록 과대적합 방지 효과
'lambda_l2' : lambda_l2 , # L2 규제 조정값 , 값이 클 수록 과대적합 방지 효과
'feature_fraction' : feature_fraction , # 개별 트리를 훈련할 때 사용할 피처 샘플링 비율
'bagging_fraction' : bagging_fraction, # 개별 트리를 훈련할 때 사용할 배깅 데이터 샘플링 비율
'min_child_samples' : int(round(min_child_samples)) , # 말단 노드가 되기 위해 필요한 최소 데이터 개수, 값이 클수록 과대적합 방지
'min_child_weight' : min_child_weight, # 과대적합 방지 위한 값
'feature_pre_filter' : False} #
#하이퍼파라미터도 추가
params.update(fixed_params)
print('하이퍼파라미터 : ' , params)
# LightGBM 모델 훈련
lgb_model = lgb.train(params = params , # 훈련용 하이퍼파라미터
train_set = bayes_dtrain, # 훈련 데이터셋
num_boost_round= 2500, #부스팅 반복횟수
valid_sets= bayes_dvalid, # 성능 평가용 검증 데이터 셋
feval = gini, # 검증용 평가지표
early_stopping_rounds= 300, # 조기종료 조건
verbose_eval= False) # 계속 점수 출력
# 검증 데이터로 예측 수행
preds = lgb_model.predict(X_valid)
# 지니계수 계산
gini_score = eval_gini(y_valid, preds)
print(f'지니계수 : {gini_score}\n')
return gini_score
=====================
# 베이지안 최적화를 수행할 하이퍼파라미터
params = {'num_leaves' : int(round(num_leaves)) , # 개발 트리가 가질 수 있는 최대 말단 노드 개수, 트리 복잡도 결정 , 값이 클수록 좋다.
'lambda_l1' : lambda_l1, # L1 규제 조정값 , 값이 클 수록 과대적합 방지 효과
'lambda_l2' : lambda_l2 , # L2 규제 조정값 , 값이 클 수록 과대적합 방지 효과
'feature_fraction' : feature_fraction , # 개별 트리를 훈련할 때 사용할 피처 샘플링 비율
'bagging_fraction' : bagging_fraction, # 개별 트리를 훈련할 때 사용할 배깅 데이터 샘플링 비율
'min_child_samples' : int(round(min_child_samples)) , # 말단 노드가 되기 위해 필요한 최소 데이터 개수, 값이 클수록 과대적합 방지
'min_child_weight' : min_child_weight, # 과대적합 방지 위한 값
'feature_pre_filter' : False} #
==> 최적화할 하이퍼파라미터 정의 : 인수로 받은 하이퍼파라미터의 값(범위)를 그대로 대입한다.
==> num_leaves 와 min_child_samples는 정수여야 한다. ==> 베이지안 최적화를 수행하면 하이퍼파라미터 지정 범위 내 실숫값을 탐색하기 때문에 eval_function()에 인수로 전달되는 값도 모두 실수형이 된다.
#하이퍼파라미터도 추가
params.update(fixed_params)
print('하이퍼파라미터 : ' , params)
==> params 는 딕셔너리 타입이기 때문에 update() 함수로 원소(fixed_params)를 추가한다.
# LightGBM 모델 훈련
lgb_model = lgb.train(params = params , # 훈련용 하이퍼파라미터
train_set = bayes_dtrain, # 훈련 데이터셋
num_boost_round= 2500, #부스팅 반복횟수
valid_sets= bayes_dvalid, # 성능 평가용 검증 데이터 셋
feval = gini, # 검증용 평가지표
early_stopping_rounds= 300, # 조기종료 조건
verbose_eval= False) # 계속 점수 출력
# 검증 데이터로 예측 수행
preds = lgb_model.predict(X_valid)
==> 앞에서 지정하 하이퍼파라미터를 이용하여 LightGBM 모델을 훈련한 뒤 검증 데이터로 예측을 수행한다.
# 지니계수 계산
gini_score = eval_gini(y_valid, preds)
print(f'지니계수 : {gini_score}\n')
return gini_score
==> 예측 확률값(preds)과 검증 데이터의 실제 타깃값(y_valid)을 이용해 지니계수를 계산한다.'
8. 성능개선 1 : LightGBM 모델_베이지안 최적화 실행
from bayes_opt import BayesianOptimization
# 베이지안 최적화 객체 생성
optimizer = BayesianOptimization(f = eval_function, # 평가지표 계산 함수
pbounds = param_bounds, # 하이퍼파라미터 범위
random_state = 0 )
# 베이지안 최적화 수행
optimizer.maximize(init_points= 3 , n_iter = 6) # init_points 는 무작위로 하이퍼파라미터를 탐색하는 횟수, n_iter는 베이지안 최적화 반복 횟수
==> init_points 는 무작위로 하이퍼파라미터를 탐색하는 횟수
==> n_iter 는 베이지안 최적화 반복횟수
==> 베이지안 최적화 수행
==> 모델 훈련을 총 9번 반복하여 베이지안 최적화가 진행된다.
| iter | target | baggin... | featur... | lambda_l1 | lambda_l2 | min_ch... | min_ch... | num_le... |
# 평가함수 점수가 최대일 대 하이퍼파라미터
max_params = optimizer.max['params']
max_params
# 값이 고정된 하이퍼파라미터 추가
max_params.update(fixed_params)
9. 최적 하이퍼파라미터를 통한 모델 훈련 및 성능 검증
from sklearn.model_selection import StratifiedKFold
# 층화 K 폴드 교차 검증기 생성
folds = StratifiedKFold(n_splits=5 , shuffle = True , random_state= 1991)
# OOF 방식으로 훈련된 모델로 검증 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_val_preds = np.zeros(X.shape[0])
# OOF 방식으로 훈련된 모델로 테스트 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_test_preds = np.zeros(X_test.shape[0])
# OOF 방식으로 모델 훈련 ,검증 , 예측
for idx, (train , valid_idx) in enumerate(folds.split(X,y)):
# 각 폴드를 구분하는 문구 출력
print('#'*40 , f'폴드 {idx+1} / 폴드 {folds.n_splits}' , '#'*40)
X_train , y_train = X[train_idx] , y[train_idx] # 훈련용 데이터
X_valid , y_valid = X[valid_idx] , y[valid_idx] # 검증용 데이터
# LightGBM 전용 데이터셋 생성
dtrain = lgb.Dataset(X_train , y_train) # LightGBM 전용 훈련 데이터셋
dvalid = lgb.Dataset(X_valid , y_valid) # LightGBM 전용 검증 데이터셋
# LightGBM 모델 훈련
lgb_model = lgb.train(params = max_params , # 최적 하이퍼파라미터
train_set = dtrain, # 훈련 데이터 셋
num_boost_round= 2500, # 부스팅 반복 횟수
valid_sets= dvalid , # 성능 평가용 검증 데이터셋
feval = gini, # 검증용 평가지표
early_stopping_rounds= 300, # 조기종료 조건
verbose_eval = 100) # 100 번째 마다 점수 출력
# 테스트 데이터를 활용해 OOF 예측
oof_test_preds += lgb_model.predict(X_test) / folds.n_splits
# 모델 성능 평가를 위한 검증 데이터 타깃값 예측
oof_val_preds[valid_idx] += lgb_model.predict(X_valid)
oof_test_preds_lgb = oof_test_preds
# 검증 데이터 예측 확률에 대한 정규화 지니계수
gini_score = eval_gini(y_valid, oof_val_preds[valid_idx])
print(f'폴드 {idx+1} 지니계수 : {gini_score}\n')
==> lgb_model = lgb.train(params = max_params) 로 변경
https://knowallworld.tistory.com/397
[PYTHON - 머신러닝_캐글_실습-02]안전 운전자 예측 모델링★베이지안 최적화★LightGBM★K-폴드 교차
1. 베이스라인 모델 1) 데이터 불러오기 2) 피처 엔지니어링 --> 명목형 피처 원-핫 인코딩 --> 필요 없는 피처 제거 3) 평가지표 계산 함수 작성 --> 정규화 지니계수 4) 모델 훈련 --> 모델 : LightGBM -->
knowallworld.tistory.com
print('OOF 검증 데이터 지니계수 :' , eval_gini(y, oof_val_preds))
==> 검증 데이터로 예측한 확률과 실제 타깃값의 지니계수
'머신러닝 > 캐글_실습' 카테고리의 다른 글
[PYTHON - 머신러닝_캐글_실습-02]안전 운전자 예측 모델링★베이지안 최적화★LightGBM★K-폴드 교차검증
1. 베이스라인 모델
1) 데이터 불러오기
2) 피처 엔지니어링
--> 명목형 피처 원-핫 인코딩
--> 필요 없는 피처 제거
3) 평가지표 계산 함수 작성
--> 정규화 지니계수
4) 모델 훈련
--> 모델 : LightGBM
--> 훈련 / 검증 : Out of Fold (OOF)
--> 성능 검증
2. 피처 엔지니어링
all_data = pd.concat([train , test] , ignore_index= True)
all_data = all_data.drop('target' , axis = 1) # 타깃값 제거
all_data
==> 훈련 데이터와 테스트 데이터의 합치기
==> 두 데이터에 동일한 인코딩 적용하기 위해
3. 명목형 피처 ONE-HOT 인코딩
from sklearn.preprocessing import OneHotEncoder
# 명목형 피처 추출
cat_features = [feature for feature in all_features if 'cat' in feature]
# 이름에 cat이 포함된 피처가 명목형 피처이다.
onehot_encoder = OneHotEncoder() # 원-핫 인코더 객체 생성
# 인코딩
encoded_cat_matrix = onehot_encoder.fit_transform(all_data[cat_features])
encoded_cat_matrix
==> 이름에 cat이 포함된 피처가 명목형 피처이다.
from scipy import sparse
all_data_sprs = sparse.hstack([sparse.csr_matrix(all_data[remaining_features]) , encoded_cat_matrix] , format= 'csr')
all_data_sprs
==> 명목형 피처에서 제거하고 남은 열에 대하여 csr_matrix()로 CSR 처리 이후 hstack()으로 행렬을 수평 방향으로 합친다.
num_train = len(train) # 훈련 데이터 개수
# 훈련 데이터와 테스트 데이터 나누기
X = all_data_sprs[:num_train]
X_test = all_data_sprs[num_train :]
y = train['target'].values
==> 전체 데이터를 훈련 데이터와 테스트 데이터로 나눈다.
==> 타깃값 y에 할당한다.
1> LightGBM 용 gini() 함수
def gini(preds , dtrain):
labels = dtrain.get_label()
return 'gini' , eval_gini(labels , preds ) , True
# 'gini' : 평가지표이름 , eval_gini(labels,preds) : 평가점수 , True : 평가 점수가 높을수록 좋은지 여부
4. 모델 훈련 및 성능 검증
https://knowallworld.tistory.com/386
[PYTHON - 머신러닝_캐글_교차검증]★K-폴드 교차검증★충화 K-폴드 교차검증★folds.split(data)★
1. 교차검증 ==> 일반적으로 훈련 데이터로 모델을 훈련하고, 테스트 데이터로 예측해 모델 성능을 측정한다. ==> 모델을 훈련만 하고, 성능을 검증해 보지 않으면 2가지 문제 발생 ㉠ 모델이 과대
knowallworld.tistory.com
==> K폴드 교차 검증
1> OOF(Out of Fold prediction) 예측 방식
==> K 폴드 교차 검증을 수행하면서 각 폴드마다 테스트 데이터로 예측하는 방식이다.
==> K 폴드 교차 검증을 하면서 폴드마다
1) 훈련 데이터로 모델을 훈련하고,
2)검증 데이터로 모델 성능을 측정하며 ,
3) 테스트 데이터로 최종 타깃 확률도 예측한다. 훈련된 모델로 마지막에 한 번만 예측하는 것이 아니다. 각 폴드별 모델로 여러번 예측해 평균을 내는 방식이다.
2> OOF(Out of Fold prediction) 예측 절차
1) 전체 훈련 데이터를 K개 그룹으로 나눈다.
2) K개 그룹 중 한 그룹은 검증 데이터, 나머지 K-1 그룹은 훈련 데이터로 지정한다.
3) 훈련 데이터로 모델을 훈련한다.
4) 훈련된 모델을 이용해 검증 데이터로 타깃 확률을 예측하고, 전체 테스트 데이터로도 타깃값 확률을 예측한다.
5) 검증 데이터로 구한 예측 확률과 테스트 데이터로 구한 예측 확률을 기록한다.
6) 검증 데이터를 다른 그룹으로 바꿔가며 2~5번 절차를 K번 반복한다.
7) K개 그룹의 검증 데이터로 예측한 확률을 훈련 데이터 실제 타깃값과 비교해 성능 평가점수를 계산한다.
8) 테스트 데이터로 구한 K개 예측 확률의 평균을 구한다.
3> OOF(Out of Fold prediction) 방식으로 LightGBM 훈련
==> 타깃값이 불균형하므로, K폴드가 아닌 층화 K폴드를 수행한다.
# OOF 방식으로 LightGBM 훈련
from sklearn.model_selection import StratifiedKFold
# 층화 K 폴드 교차 검증기
folds = StratifiedKFold(n_splits= 5 , shuffle= True , random_state= 1991)
==> n_splits 파라미터로 전달한 수만큼 폴드를 나눈다. 여기서는 5개로 나누었다. shuffle = True 를 전달하면 폴드를 나눌때 데이터를 섞어준다.
params = {'objective' : 'binary' , 'learning_rate' : 0.01 , 'force_row_wise' : True , 'random_state' : 0}
==> LightGBM의 하이퍼파라미터 설정
== > 이진분류 문제이므로 objective 파라미터는 binary로 설정했다. 학습률은 0.01로, 랜덤 스테이트 값은 9으로 설정했다. force_row_wise : True 는 경고 문구를 없애려고 추가한 파라미터이다.
# OOF 방식으로 훈련된 모델로 검증 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_val_preds = np.zeros(X.shape[0])
# ==> oof_val_preds 는 검증 데이터를 활용해 예측한 확률값을 저장하는 배열이다. K 폴드로 나누어도 훈련 데이터 전체가 결국엔 한 번씩 검증 데이터로 활용된다. 따라서 oof_val_preds 배열 크기는 훈련 데이터와 같아야 한다.
# 훈련 데이터 개수는 X.shpae[0]으로 구한다.
# OOF 방식으로 훈련된 모델로 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_test_preds = np.zeros(X_test.shape[0])
# oof_test_preds는 테스트 데이터를 활용해 예측한 확률값을 저장하는 배열이다. 최종 제출에 사용할 값이므로 크기는 테스트 데이터와 같아야한다. 테스트 데이터 개수는 X_test.shape[0]으로 구한다.
oof_val_preds = np.zeros(X.shape[0])
==> OOF 방식으로 훈련된 모델로 검증 데이터 타깃값을 예측한 확률을 담을 1차원 배열
==> 훈련 데이터 개수로 채워진 0
oof_test_preds = np.zeros(X_test.shape[0])
==> OOF 방식으로 훈련된 모델로 테스트 데이터 타깃값을 예측한 확률을 담을 1차원 배열
5. LightGBM 모델 검증
1) folds = StratifiedKFold(n_splits= 5 , shuffle= True , random_state= 1991)
print(i for i in folds.split(X,y))
for idx, (train_idx , valid_idx) in enumerate(folds.split(X, y)): # 훈련 데이터 , 타깃 데이터
print(f'idx : {idx}')
print(f'train_idx : {train_idx}')
print(f'valid_idx : {valid_idx}')
idx : 0
train_idx : [ 0 1 3 ... 595209 595210 595211]
valid_idx : [ 2 5 15 ... 595181 595188 595190]
idx : 1
train_idx : [ 0 1 2 ... 595207 595209 595211]
valid_idx : [ 8 10 25 ... 595201 595208 595210]
2) 훈련용, 검증용 데이터 설정 , LightGBM 전용 데이터셋 생성
# 훈련용 데이터, 검증용 데이터 설정
X_train , y_train = X[train_idx] , y[train_idx] # 훈련용 데이터
X_valid , y_valid = X[valid_idx] , y[valid_idx] # 검증용 데이터
# LightGBM 전용 데이터셋 생성
dtrain = lgb.Dataset(X_train , y_train) # LightGBM 전용 훈련 데이터 셋
dvalid = lgb.Dataset(X_valid , y_valid) # LightGBM 전용 검증 데이터 셋
3) LightGBM 모델 훈련
# LightGBM 모델 훈련
lgb_model = lgb.train(params = params , # 훈련용 하이퍼파라미터
train_set = dtrain, # 훈련 데이터 셋
num_boost_round = 1000, # 부스팅 반복 횟수
valid_sets= dvalid , # 성능 평가용 검증 데이터 셋
feval = gini, # 검증용 평가지표
early_stopping_rounds = 100, # 조기종료 조건
verbose_eval = 100 ) # 100번째마다 점수 출력
4) 테스트 데이터를 활용한 OOF 예측
# 테스트 데이터를 활용해 OOF 예측
oof_test_preds += lgb_model.predict(X_test)/folds.n_splits
# 모델 성능 평가를 위한 검증 데이터 타깃값 예측
oof_val_preds[valid_idx] += lgb_model.predict(X_valid)
==> oof_test_preds 는 훈련된 모델에 테스트 데이터를 주어 타깃 확률값을 예측한다.
==> folds.n_splits = 5
==> 폴드가 5번 반복되면 oof_val_preds 내 모든 값이 검증 데이터 예측 확률로 업데이트가 된다.
5) 정규화 지니계수 계산
# 검증 데이터 예측 확률에 대한 정규화 지니계수
gini_score = eval_gini(y_valid , oof_val_preds[valid_idx])
==> 모델 훈련시 verbose_eval = 100 으로 설정해서 100번째 이터레이션마다 성능 평가점수 출력
==> 성능 평가점수는 검증용 데이터로 계산한 값이다.
==> 로그 손실(logloss)는 이진 분류 할때 LightGBM의 기본평가 지표이다.
==> 모델 훈련 시 feval 파라미터에 전달한 gini() 함수의 계산값을 오른쪽에 출력, feval 파라미터에 사용자 정의 함수 전달하지 않으면 logloss만 출력한다.
==> 이터레이션 반복할 수 록 지니계수가 커진다.
==> num_boost_rounds = 1000 전달 ==> 부스팅 최대 반복 횟수가 1000번인데 다 채우지 못하고 조기종료한다.
==> early_stopping_rounds = 100으로 지정하여 , 100번 연속으로 지니계수가 최대값을 갱신하지 못하면 종료한다.
6. 성능 개선 1 : LightGBM 모델
1> 파생 피처 추가
all_data['num_missing'] = (all_data == -1).sum(axis = 1)
==> 첫번째 , 한 데이터가 가진 결측값 개수를 파생 피처로 만들어본다. -1 이 결측값이므로 결측값 개수를 구하려면 -1 개수를 구하면 된다.
==> 데이터 하나당 결측값 개수를 파생 피처로 추가
# 명목형 피처 , calc 분류의 피처를 제외한 피처
remaining_features = [feature for feature in all_features if ('cat' not in feature and 'calc' not in feature)]
# num_missing을 remaining_features 에 추가
remaining_features.append('num_missing')
remaining_features
==> 명목형 피처 , calc 분류의 피처를 제외한 피처
remaining_features = ['ps_ind_01', 'ps_ind_03', 'ps_ind_06_bin', 'ps_ind_07_bin', 'ps_ind_08_bin', 'ps_ind_09_bin', 'ps_ind_10_bin', 'ps_ind_11_bin', 'ps_ind_12_bin','ps_ind_13_bin','ps_ind_14','ps_ind_15','ps_ind_16_bin','ps_ind_17_bin','ps_ind_18_bin','ps_reg_01','ps_reg_02','ps_reg_03','ps_car_11','ps_car_12','ps_car_13','ps_car_14','ps_car_15', 'num_missing']
ind_features = [feature for feature in all_features if 'ind' in feature]
is_first_feature = True
for ind_feature in ind_features:
if is_first_feature:
all_data['mix_ind'] = all_data[ind_feature].astype(str) + '-'
is_first_feature = False
else:
all_data['mix_ind'] += all_data[ind_feature].astype(str) + '-'
==> 모든 값이 '-'로 연결되었다.
cat_count_features = []
for feature in cat_features+['mix_ind']:
val_counts_dict = all_data[feature].value_counts().to_dict()
all_data[f'{feature}_count'] = all_data[feature].apply(lambda x: val_counts_dict[x])
cat_count_features.append(f'{feature}_count')
encoded_cat_matrix : 원-핫 인코딩된 명목형 피처
remaining_features : 명목형 피처와 calc 분류의 피처를 제외한 피처들(+ num_missing)
cat_count_features : mix_ind를 포함한 명목형 피처의 고윳값별 개수 파생 피처
from scipy import sparse
# 필요 없는 피처들
drop_features = ['ps_ind_14' , 'ps_ind_10_bin' , 'ps_ind_11_bin' , 'ps_ind_12_bin' , 'ps_ind_13_bin' , 'ps_car_14']
# remaining_features , cat_count_features 에서 drop_features를 제거한 데이터
all_data_remaining = all_data[remaining_features + cat_count_features].drop(drop_features , axis = 1)
# 데이터 합치기
all_data_sprs = sparse.hstack([sparse.csr_matrix(all_data_remaining) , encoded_cat_matrix] ,format = 'csr')
==> scipy 라이브러리의 sparse 모듈로 csr 형태로 행렬 합치기
2> 데이터 나누기
num_train = len(train) # 훈련 데이터 개수
# 훈련 데이터와 테스트 데이터 나누기
X = all_data_sprs[:num_train]
X_test = all_data_sprs[num_train :]
y = train['target'].values
3> 하이퍼파라미터 최적화
==> 성능이 우수한 모델을 만들기 위하여 베이지안 최적화 기법을 활용하여 하이퍼파라미터를 조정해야한다.'
베이지안 최적화 : 사전 정보를 바탕으로 최적 하이퍼파라미터 값을 확률적으로 추정하며 탐색하는 기법
https://knowallworld.tistory.com/390
[PYTHON - 머신러닝_캐글_모델]★하이퍼파라미터 최적화★그리드 서치★랜덤서치★베이지안 최적
1. 하이퍼파라미터 ==> 하이퍼파라미터는 사용자가 직접 설정해야 하는 값이다. ==> 모델이 좋은 성능을 내려면 하이퍼파라미터가 어떤 값을 가지면 좋을지를 찾는 작업이 하리어파라미터 최적화
knowallworld.tistory.com
import lightgbm as lgb
from sklearn.model_selection import train_test_split
# 8:2 비율로 훈련 데이터, 검증 데이터 분리(베이지안 최적화 수행용)
X_train , X_valid , y_train , y_valid = train_test_split(X,y, test_size=0.2 , random_state=0)
# 베이지안 최적화용 데이터셋
bayes_dtrain = lgb.Dataset(X_train , y_train)
bayes_dvalid = lgb.Dataset(X_valid, y_valid)
==> 베이지안 최적화용 데이터 셋
하이퍼파라미터 범위 설정 :
1> 하이퍼 파라미터 범위를 점점 좁히는 방법 :
ex ) 0~1 범위의 하이퍼파라미터 찾고 다음에는 0.5주변으로 범위를 좁히기
# 베이지안 최적화를 위한 하이퍼파라미터 범위
param_bounds = {'num_leaves' : (30 , 40) , # 개별 트리가 가질 수 있는 최대 말단 노드 개수 , 트리 복잡도 결정, 값이 클수록 좋다.
'lambda_l1' : (0.7 , 0.9), # L1 규제 조정값 , 값이 클수록 과대적합 방지 효과
'lambda_l2' : (0.9 , 1), # L2 규제 조정값 , 값이 클수록 과대적합 방지 효과
'feature_fraction' : (0.6 , 0.7), # 개별 트리를 훈련할 때 사용할 피처 샘플링 비율
'bagging_fraction' : (0.6 , 0.9), # 개별 트리를 훈련할 때 사용할 데이터 샘플링 비율
'min_child_samples' : (6 , 10) , # 말단 노드가 되기 위해 필요한 최소 데이터 개수 , 값이 클수록 과대적합 방지
'min_child_weight' : (10 , 40)} # 과대적합 방지 위한 값
# 값이 고정된 하이퍼파라미터
fixed_params = {'objective' : 'binary' , # 훈련 목적 , 회귀에서는 'regression' , 이진분류에서는 'binary' , 다중분류에서는 'multiclass' 사용
'learning_rate' : 0.005, # 학습률( 부스팅 이터레이션을 반복하면서 모델을 업데이트하는 데 사용 되는 비율)
'bagging_freq' : 1, # 배깅 수행 빈도, 몇번의 이터레이션마다 배깅 수행할 지 결정
'force_row_wise' : True, # 메모리 용량이 충분하지 않을 때 메모리 효율을 높이는 파라미터
'random_state' : 1991} # 랜덤 시드값 (코드를 반복 실행해도 같은 결과가 나오게 지정하는 값)
'머신러닝 > 캐글_실습' 카테고리의 다른 글
[PYTHON - 머신러닝_캐글_실습-01]안전 운전자 예측★결측값 셀땐 missingno★서브플롯 편하게 하고싶으면gridspec★연속형 데이터 나누는데 사용하는 pd.cut()★
1. 주제
https://www.kaggle.com/competitions/porto-seguro-safe-driver-prediction/data
Porto Seguro’s Safe Driver Prediction | Kaggle
www.kaggle.com
==> 자동차 보험과 관련해서 운전자가 보험금을 청구할 확률을 정확히 예측하는 모델 생성
2. 탐색적 데이터 분석(EDA)
train.info()
#ps
#ind,reg , car ,calc ==> 분류
#01, 02 ,03 ==> 분류별 일련번호
# _bin , _cat ==> 이진피처 , 명목형 피처 , 생략시 순서형 피처 또는 연속형 피처
==> 요약표에 non-null 이라 되어있지만 , -1로 표시되어 있으므로, -1을 np.NaN으로 변환한 다음 개수를 세야한다.
1. 결측값 파악
import numpy as np
import missingno as msno
# 훈련데이터 복사본에서 -1을 np.NaN으로 변환
train_copy = train.copy().replace(-1 , np.NaN)
# 결측값 시각화(처음 28개만)
msno.bar(df=train_copy.iloc[: , 1:29] , figsize=(13,6))
==> ps_reg_03 , ps_car_03_cat , ps_car_05_cat 에 결측값이 존재한다.
msno.matrix(df=train_copy.iloc[:, 1:29] , figsize=(13,6))
==> 결측값 매트릭스 형태로 시각화(22개의 결측값 없는 열수 , 전체 28개 열)
2. 피처 요약표
def resumetable(df):
print(f'데이터셋 형상 : {df.shape}')
summary = pd.DataFrame(df.dtypes , columns = ['데이터 타입'])
summary['결측값 개수'] = (df == -1).sum().values # 피처별 -1 개수
summary['고윳값 개수'] = df.nunique().values
summary['데이터 종류'] = None
for col in df.columns:
if 'bin' in col or col == 'target':
summary.loc[col , '데이터 종류'] = '이진형'
elif 'cat' in col:
summary.loc[col , '데이터 종류'] = '명목형'
elif df[col].dtype == 'float64':
summary.loc[col , '데이터 종류'] = '연속형'
elif df[col].dtype == 'int64':
summary.loc[col, '데이터 종류'] = '순서형'
return summary
summary = resumetable(train)
summary
==> (df == -1).sum().values ==> 피처의 값이 -1인 값들은 더한 값
==> df.nunique().values ==> 피처의 고윳값 개수들에 대한 값
==> bin(이진피처) , 피처이름이 target 일때 , 데이터 종류 열을 이진형으로 변경
3. 데이터 시각화
https://knowallworld.tistory.com/203
Plt, Fig, Seaborn 이해[Python]★기초통계학-[Chapter02 - 연습문제_02]
p46 2번문제 어느 대학에서 교내 음주의 찬성 여부를 재학생 100명 대상으로 조사 a = '찬성 찬성 찬성 무응답 찬성 무응답 찬성 반대 무응답 찬성 반대 무응답 찬성 반대 찬성 반대 찬성 찬성 무응
knowallworld.tistory.com
==> 막대 그래프 조정하기
def write_percent(ax , total_size):
# 도형 객체를 순회하며 막대 그래프 상단에 타깃값 비율 표시
for patch in ax.patches:
height = patch.get_height() # 도형 높이(데이터 개수)
width = patch.get_width() # 도형 너비
left_coord = patch.get_x() # 도형 왼쪽 테두리의 x축 위치
percent = height/total_size*100 # 타깃값 비율
# (x, y) 좌표에 텍스트 입력
ax.text(left_coord + width/2.0, # x축 위치
height + total_size*0.001, # y축 위치
'{:1.1f}%'.format(percent), # 입력 텍스트
ha = 'center' ) # 가운데 정렬
mpl.rc('font' , size = 15)
plt.figure(figsize=(7,6))
ax = sns.countplot(x='target', data = train)
write_percent(ax , len(train)) # 비율 표시
ax.set_title('Target Distribution')
==> 전체 운전자 중 3.6%만 보험금을 청구했다는 뜻이다. ==> 타깃값이 불균형하다.
==> 타깃값이 불균형하므로 비율이 작은 타깃값 1을 잘 예측하는 것이 중요하다.
1> 이진 피처
import matplotlib.gridspec as gridspec
def plot_target_ratio_by_features(df, features , num_rows , num_cols , size=(12,18)):
# features 는 데이터 종류
mpl.rc('font' , size= 9)
plt.figure(figsize = size) # 전체 Figure 크기 설정
grid = gridspec.GridSpec(num_rows , num_cols) # 서브플롯 배치
plt.subplots_adjust(wspace=0.3 , hspace=0.3) # 서브플롯 좌우/상하 여백 설정
for idx, feature in enumerate(features):
ax = plt.subplot(grid[idx])
# ax 축에 고윳값별 타깃값 1 비율을 막대 그래프로 그리기
sns.barplot(x=feature , y='target' , data = df , palette='Set2' , ax =ax)
==> gridspec.GridSpec(num_rows , num_cols) ==> 서브플롯 배치
==>plt.subplots_adjust(wspace=0.3 , hspace=0.3) ==> 서브플롯 좌우/상하 여백 설정
bin_features = summary[summary['데이터 종류'] == '이진형'].index # 이진 피처
# 이진 피처 고윳값별 타깃값 1 비율을 막대 그래프로 그리기
plot_target_ratio_by_features(train , bin_features , 6 , 3) # 6행 3열로 배치
==> plot_target_ratio_by_features( 데이터프레임 , x축 값 , 6행 , 3열)
==> ps_calc_06_bin 의 고윳값 0의 경우 타깃값 1 비율이 0.04 , 고윳값 1의 경우 타깃값 1 비율이 0.03 정도 된다.
==> 고윳값 별로 타깃값 비율이 다르므로, 타깃값을 추정하는 예측력이 있다.
==> 고윳값 별로 타깃값 비율의 차이가 없는 피처들과 신뢰구간이 넓은 피처들은 예측력이 없으므로 삭제한다.
2> 명목형 피처
nom_features = summary[summary['데이터 종류'] == '명목형'].index # 명목형 피처
plot_target_ratio_by_features(train , nom_features , 7 , 2) # 7행 2열
1) ps_ind_02_cat : 결측값 -1이 다른 고윳값들 보다 타깃값 1 비율이 크다.
==> 결측값을 다른 값으로 대체하면 모델 성능이 더 나빠질 수 있다. 결측값 자체가 타깃값에 대한 예측력이 있기 때문이다.
2) ps_car_02_cat : 고윳값 -1일때 타깃값 1 비율은 0%이다. 피처 값이 -1 이면 타깃값이 0이라고 판단해도 된다.
ord_features = summary[summary['데이터 종류'] == '순서형'].index # 순서형 피처
plot_target_ratio_by_features(train , ord_features , 8,2 ,(12,20)) # 8행 2열
==> ps_ind_14 의 경우 신뢰구간이 상당히 넓어 통계적 유효성이 떨어진다.
==> ps_calc_04 부터 ps_calc_14 까지는 모두 고윳값별 타깃값 비율이 거의 비슷하여 통계적 유효성이 떨어진다.
3> 연속형 피처
==> 연속형 피처는 연속된 값이므로 고윳값이 굉장히 많다. 고윳값별 타깃값 1 비율을 구하기 힘들다.
==> 값을 몇 개의 구간으로 나누어서 구간별 타깃값 1 비율을 알아본다.
==> 연속형 데이터를 구간으로 나누려면 판다스의 cut() 함수 활용
pd.cut([1.0 , 1.5 , 2.1 , 2.7 , 3.5 , 4.0], 3) # cut()함수를 활용해 여러 개의 값을 3개 구간으로 나누었다.
==> 연속형 데이터를 범주형 데이터로 바꾸는 효과가 있다.
cont_features = summary[summary['데이터 종류'] == '연속형'].index # 연속형 피처
plt.figure(figsize= (12,16))
grid = gridspec.GridSpec(5,2)
plt.subplots_adjust(wspace= 0.2 , hspace = 0.4) # 서브플롯 간 여백 설정
for idx , cont_feature in enumerate(cont_features):
#값을 5개 구간으로 나누기
train[cont_feature] = pd.cut(train[cont_feature] , 5)
ax = plt.subplot(grid[idx]) # 분포도를 그릴 서브플롯 설정
sns.barplot(x = cont_feature , y= 'target' , data = train , palette= 'Set2' , ax = ax)
ax.tick_params(axis ='x' , labelrotation =10 ) # x축 라벨 회전
==> 고윳값별로 타깃값 비율이 서로 다른 것들만 남기고, 비슷한것들은 삭제 한다!
train_copy = train_copy.dropna() # np.NaN 값 삭제
plt.figure(figsize =(10 , 8))
cont_corr = train_copy[cont_features].corr() # 연속형 피처 간 상관관계
sns.heatmap(cont_corr, annot = True , cmap = 'OrRd') # 히트맵 그리기
==> 상관관계가 높은 피처가 있으면 삭제하는 것이 좋다. ==> 상관관계가 강하면 타깃값 예측력도 비슷하다.
출처 : 머신러닝·딥러닝 문제해결 전략
(Golden Rabbit , 저자 : 신백균)
※혼자 공부용
'머신러닝 > 캐글_실습' 카테고리의 다른 글
[PYTHON - 머신러닝_캐글_실습-02]범주형 데이터 이진분류★o
1. 원-핫 인코딩
https://knowallworld.tistory.com/384
[PYTHON - 머신러닝_캐글_데이터 인코딩]★LabelEncoder★One-Hot-Encoder
머신러닝 모델은 문자 데이터를 인식하지 못한다. 이를 문자로 구성된 범주형 데이터는 숫자로 바꿔야 한다. ==> 범주형 데이터를 숫자 형태로 바꾸는 작업을 데이터 인코딩이라고 한다. 1. 레이
knowallworld.tistory.com
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder() # 원 - 핫 인코더 생성
all_data_encoded = encoder.fit_transform(all_data) # 원 핫 인코딩 적용
all_data_encoded
'머신러닝 > 캐글_실습' 카테고리의 다른 글
[PYTHON - 머신러닝_캐글_실습-01]범주형 데이터 이진분류★subplots 활용할 수 있는 gridspec★ax.patches★교차표(crosstab)★plt.grid(visible=False)★CategoricalDtype()★
1. 분류 문제
==>분류 문제에는 타깃값(종속변수)를 예측할 수도 있고, 타깃값일 확률을 예측할 수 도 있다.
==> 타깃값이 0 이나 1 이냐가 아닌 1일 확률을 예측한다.
==> 이번 문제의 목표는 각 테스트 데이터의 타깃값이 1일 확률을 예측하는 것이다.
2. 데이터 파악
def resumetable(df):
print(f'데이터셋 형상 : {df.shape}')
summary = pd.DataFrame(df.dtypes , columns = ['데이터 타입'])
summary = summary.reset_index()
summary = summary.rename(columns = {'index' : '피처'})
summary['결측값 개수'] = df.isnull().sum().values
summary['고윳값 개수'] = df.nunique().values
summary['첫 번째 값'] = df.loc[0].values
summary['두 번째 값'] = df.loc[1].values
summary['세 번째 값'] = df.loc[2].values
return summary
resumetable(train)
1. 이진 (binary) 피처 : bin_0 ~ bin_4 ==> 고윳값 개수가 2개씩 ==> 이진 피처이다. ==> bin_0~ 2 은 데이터타입이 int로써, 실제값이 0또는 1로 구성되어있다.
==> bin_3~ 4 는 데이터타입이 object로써, 실제값이 T또는 F , Y또는 N이다.
2. 명목형(nominal) 피처 : nom_0 ~ nom_9 ==> 명목형 피처는 모두 object 타입이고, 결측값은 없다.
3. 순서형(ordinal) 피처 : ord_0 ~ ord_5
for i in range(3):
feature = 'ord_' + str(i)
print(f'{feature} 고윳값 : {train[feature].unique()}')
# 순서형 데이터들의 고윳값 출력
ord_0 고윳값 : [2 1 3]
ord_1 고윳값 : ['Grandmaster' 'Expert' 'Novice' 'Contributor' 'Master']
ord_2 고윳값 : ['Cold' 'Hot' 'Lava Hot' 'Boiling Hot' 'Freezing' 'Warm']
==> 원 핫 인코딩시 참고 사항
for i in range(3,6):
feature = 'ord_' + str(i)
print(f'{feature} 고윳값 : {train[feature].unique()}')
ord_3 고윳값 : ['h' 'a' 'i' 'j' 'g' 'e' 'd' 'b' 'k' 'f' 'l' 'n' 'o' 'c' 'm']
ord_4 고윳값 : ['D' 'A' 'R' 'E' 'P' 'K' 'V' 'Q' 'Z' 'L' 'F' 'T' 'U' 'S' 'Y' 'B' 'H' 'J'
'N' 'G' 'W' 'I' 'O' 'C' 'X' 'M']
ord_5 고윳값 : ['kr' 'bF' 'Jc' 'kW' 'qP' 'PZ' 'wy' 'Ed' 'qo' 'CZ' 'qX' 'su' 'dP' 'aP'
··········]
==> 원 핫 인코딩시 참고 사항
4. 그 외 피처 : day , month , target
print('day 고윳값 : ',train['day'].unique())
print('month 고윳값 :' , train['month'].unique())
print('target 고윳값 : ', train['target'].unique())
day 고윳값 : [2 7 5 4 3 1 6]
month 고윳값 : [ 2 8 1 4 10 3 7 9 12 11 5 6]
target 고윳값 : [0 1]
3. 데이터 시각화
import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
1. 타깃값 분포
def write_percent(ax , total_size):
# 도형 객체를 순회하며 막대 상단에 타깃값 비율 표시
for patch in ax.patches:
height = patch.get_height() # 도형 높이(데이터 개수)
width = patch.get_width() # 도형 넓이
left_coord = patch.get_x() # 도형 왼쪽 테두리의 x축 위치
percent = height/total_size*100 # 타깃값 비율
# (x,y) 좌표에 텍스트 입력
ax.text(x=left_coord + width/2.0 , y = height + total_size*0.001,
s = f'{percent : 1.1f}%', ha= 'center')
plt.figure(figsize=(7,6))
ax = sns.countplot(x='target' , data = train)
write_percent(ax , len(train)) # 비율 표시
ax.set_title('Target Distribution')
==> 타깃값의 불균형을 파악하여 부족한 타깃값에 더 집중해 모델링을 수행할 수 있다.
2. 이진 피처 분포
import matplotlib.gridspec as gridspec # 여러 그래프를 격자 형태로 배치
# 3행 2열
mpl.rc('font' , size = 12)
grid = gridspec.GridSpec(3,2) # 그래프(서브플롯)을 3행 2열로 배치
plt.figure(figsize=(10,16)) # 전체 Figure 크기 설정
plt.subplots_adjust(wspace= 0.4 , hspace= 0.3) # 서브플롯 간 좌우/상하 여백 설정
# 서브플롯 그리기
bin_features = ['bin_0' , 'bin_1' , 'bin_2' , 'bin_3', 'bin_4'] # 피처 목록
for idx, feature in enumerate(bin_features) :
ax = plt.subplot(grid[idx])
# ax 축에 타깃값 분포 카운트플롯 그리기
sns.countplot(x=feature , data = train , hue = 'target' , palette = 'pastel' , ax= ax)
# hue는 세부적으로 나눠 그릴 기준 피처, 여기서는 타깃값(target)을 전달했다.
ax.set_title(f'{feature} Distribution by Target') # 그래프 제목 설정
write_percent(ax, len(train))
==> bin_4 : 값이 Y인 데이터 중에서 target 값이 0인 데이터와 1인 데이터를 나누어 표시
3. 명목형 피처 분포
==> 명목형 피처 분포와 명목형 피처별 타깃값 1의 비율 살펴보기
pd.crosstab(train['nom_0'] , train['target'])
==> 교차표(cross-tabulation) 혹은 교차분석표는 범주형 데이터 2개를 비교 분석하는 데 사용되는 표로, 각 범주형 데이터의 빈도나 통계량을 행과 열로 결합해놓은 표를 말한다.
==> 교차분석표를 만드는 이유는 명목형 피처별 타깃값 1 비율을 구하기 위해서이다.
crosstab = pd.crosstab(train['nom_0'] , train['target'] , normalize= 'index') * 100
# 열을 기준으로 정규화하려면 normalize = 'columns'로 설정해 실행하면 된다.
# 정규화한 값(비율) 구하기.
crosstab
def get_crosstab(df , feature):
crosstab = pd.crosstab(df[feature] , df['target'] , normalize='index') * 100
crosstab = crosstab.reset_index()
return crosstab
crosstab = get_crosstab(train, 'nom_0')
crosstab
def plot_pointplot(ax , feature , crosstab):
ax2 = ax.twinx() # x축은 공유하고 y축은 공유하지 않는 새로운 축 생성
# 새로운 축에 포인트플롯 그리기
ax2 = sns.pointplot(x=feature , y =1 , data=crosstab ,
order = crosstab[feature].values , color = 'black' , legend = True)
ax2.set_ylim(crosstab[1].min()- 5 , crosstab[1].max()*1.1) # y축 범위 설정
ax2.set_ylabel('Target 1 Ratio(%)')
==> plot_pointplot()은 이미 카운트플롯이 그려진 축에 포인트플롯을 중복으로 그린다.
==> x축을 두 그래프가 공유한다.
def plot_cat_dist_with_true_ratio(df, features , num_rows , num_cols , size = (15,20) ):
plt.figure(figsize = size) # 전체 Figure 크기 설정
grid = gridspec.GridSpec(num_rows , num_cols) # 서브플롯 배치
plt.subplots_adjust(wspace = 0.45 , hspace= 0.3) # 서브플롯 좌우/상하 여백 설정
for idx ,feature in enumerate(features):
ax = plt.subplot(grid[idx])
# ax.set_xticks([])
# ax.set_yticks([])
crosstab = get_crosstab(df, feature) # 교차분석표 생성
# ax 축에 타깃값 분포 카운트플롯 그리기
sns.countplot(x = feature , data = df , order = crosstab[feature].values,
color = 'skyblue' , ax = ax)
plt.grid(visible=False)
write_percent(ax , len(df)) # 비율 표시
plot_pointplot(ax , feature , crosstab) # 포인트플롯 그리기
plt.grid(visible=False)
ax.set_title(f'{feature} Distribution') # 그래프 제목 설정
# ==> 이 함수는 인수로 받는 features 피처마다 타깃값별로 분포도를 그린다.
==> plt.grid(visible = False) 로 뒤에 그리드들 삭제하였다.
nom_features = ['nom_0', 'nom_1' , 'nom_2' , 'nom_3' ,'nom_4'] # 명목형 피처
plot_cat_dist_with_true_ratio(train , nom_features , num_rows= 3 , num_cols=2)
4. 순서형 피처 분포
ord_features = ['ord_0' , 'ord_1' , 'ord_2' , 'ord_3'] # 순서형 피처
plot_cat_dist_with_true_ratio(train , ord_features , num_rows=2 , num_cols=2 , size=(15,12))
from pandas.api.types import CategoricalDtype
ord_1_value = ['Novice' , 'Contributor' , 'Expert' , 'Master' , 'Grandmaster']
ord_2_value = ['Freezing' , 'Cold' , 'Warm' , 'Hot' , 'Boiling Hot' , 'Lava Hot']
# 순서를 지정한 범주형 데이터 타입
ord_1_dtype = CategoricalDtype(categories = ord_1_value , ordered = True)
ord_2_dtype = CategoricalDtype(categories = ord_2_value , ordered= True)
#categories: 범주형 데이터 타입으로 인코딩할 값 목록
# 데이터 타입 변경
train['ord_1'] = train['ord_1'].astype(ord_1_dtype)
train['ord_2'] = train['ord_2'].astype(ord_2_dtype)
train.dtypes
==> CategoricalDtype()을 적용하여 피처 자체의 순서를 지정한다.
plot_cat_dist_with_true_ratio(train , ord_features , num_rows=2 , num_cols=2 , size=(15,12))
==> ord_0은 숫자 크기 순으로, ord_1 과 ord_2 는 지정된 순서대로, ord_3는 알파벳 순으로 정렬됐다.
==> 이 결과로부터 고윳값 순서에 따라 타깃값 1 비율도 비례해서 커진다는 것을 확인할 수 있다.
4. 날짜형 피처 분포
date_features = ['day' , 'month']
plot_cat_dist_with_true_ratio(train , date_features , num_rows= 2, num_cols= 1 , size=(10,10))
# day 피처는 7개인 걸로 보아 요일을 의미한다고 추측할 수 있다.
# 1에서 4로 갈수록 타깃값 1 비율이 줄어들고, 4에서 7로 갈 수록 비율이 늘어난다.
출처 : 머신러닝·딥러닝 문제해결 전략
(Golden Rabbit , 저자 : 신백균)
※혼자 공부용