๋Ÿฌ๋‹์Šคํ‘ผ์ฆˆ ์ˆ˜์—… ์ •๋ฆฌ 

 

 

< ์ด์ „ ๊ธ€ > 

https://silvercoding.tistory.com/67

 

[Bank Marketing๋ฐ์ดํ„ฐ ๋ถ„์„] 2. python ๋ถ€์ŠคํŒ… Boosting, XGBoost ์‚ฌ์šฉ

๋Ÿฌ๋‹์Šคํ‘ผ์ฆˆ ์ˆ˜์—… ์ •๋ฆฌ < ์ด์ „ ๊ธ€ > https://silvercoding.tistory.com/66 https://silvercoding.tistory.com/65 https://silvercoding.tistory.com/64 https://silvercoding.tistory.com/63?category=967543 https..

silvercoding.tistory.com

 

 


 

'๊ฒฐ๋ก ์ด ๋ฌด์—‡์ธ์ง€' ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๊ฒƒ์€ ๋ฐ์ดํ„ฐ์‚ฌ์ด์–ธํ‹ฐ์ŠคํŠธ๋กœ์„œ์˜ ์ค‘์š”ํ•œ ์—…๋ฌด์ด๋‹ค. 

์˜ˆ์ธก ๊ฒฐ๊ณผ๋งŒ ๋ณด๊ณ ๋Š” ๋ชจ๋ธ์ด ์–ด๋–ค ํŒจํ„ด์„ ์ด์šฉํ•˜์—ฌ ์˜ˆ์ธก์„ ์‹คํ–‰ํ•˜๊ฒŒ ๋˜์—ˆ๋Š”์ง€, ์™œ ๊ทธ๋ ‡๊ฒŒ ์˜ˆ์ธกํ–ˆ๋Š”์ง€ ์„ค๋ช…ํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ๋‹ค๋ฅธ ๋ถ„์•ผ์˜ ํ˜‘์—…์ž๋“ค์€ ์‹ ๋ขฐ๋ฅผ ์žƒ๊ฒŒ๋  ๊ฒƒ์ด๋‹ค. 

๋น„์ฆˆ๋‹ˆ์Šค์˜ ๊ด€์ ์—์„œ ์˜ˆ๋ฅผ ๋“ค์–ด๋ณธ๋‹ค. ๋จธ์‹ ๋Ÿฌ๋‹์„ ํ†ตํ•˜์—ฌ ์˜ํ™” ํฅํ–‰์„ฑ์ ์„ ์˜ˆ์ธกํ•˜๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ํฅํ–‰ ์‹คํŒจ๋ผ๋Š” ์˜ˆ์ธก์ด ๋‚˜์™”๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ, ์–ด๋–ป๊ฒŒ ํฅํ–‰์‹คํŒจ๋ฅผ ๋ง‰์„ ๊ฒƒ์ด๋ƒ๊ณ  ์งˆ๋ฌธ์ด ๋“ค์–ด์˜ฌ ์ˆ˜๋„ ์žˆ๋‹ค. ๊ธฐ์กด์˜ ์ทจ์•ฝ์ ์„ ๋ณด์™„ํ•˜์ง€ ๋ชปํ•œ๋‹ค๋ฉด ๋น„์ฆˆ๋‹ˆ์Šค์˜ ๊ด€์ ์—์„œ ์˜๋ฏธ๊ฐ€ ์—†๋‹ค. 

 

๋”ฐ๋ผ์„œ ๊ฒฐ๊ณผ๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ์•„์ฃผ ์ค‘์š”ํ•˜๋‹ค. ์ด ๋•Œ ๋ณ€์ˆ˜์ค‘์š”๋„๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ์ธก์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์นœ ๋ณ€์ˆ˜์™€, ํŠน์ • ๋ณ€์ˆ˜๊ฐ€ ์–ด๋–ป๊ฒŒ ์˜ํ–ฅ์„ ๋ฏธ์ณค๋Š”์ง€ ์„ฌ์„ธํ•˜๊ฒŒ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค. 


๋ณ€์ˆ˜์ค‘์š”๋„

- ๋ชจ๋ธ์— ํ™œ์šฉํ•œ input ๋ณ€์ˆ˜ ์ค‘์—์„œ ์–ด๋–ค ๊ฒƒ์ด target ๊ฐ’์— ๊ฐ€์žฅ ํฐ ์˜ํ–ฅ์„ ๋ฏธ์ณค๋‚˜? 
- ํ•ด๋‹น ์ค‘์š”๋„๋ฅผ ์ˆ˜์น˜ํ™”์‹œํ‚จ ๊ฒƒ
- treeํ˜• ๋ชจ๋ธ (์˜์‚ฌ๊ฒฐ์ •๋‚˜๋ฌด, ๋žœ๋คํฌ๋ ˆ์ŠคํŠธ) ์—์„œ ๊ณ„์‚ฐ ๊ฐ€๋Šฅ 

 

์ด์ „ ๊ธ€์˜ treeํ˜• ๋ชจ๋ธ์ธ random forest์™€ xgboost์—์„œ ๋ณ€์ˆ˜์ค‘์š”๋„ ๊ณ„์‚ฐ์„ ์‹คํ–‰ํ–ˆ์—ˆ๋‹ค.  

(์ฐธ๊ณ )  ๋ฐฐ๊น…  ๋ถ€์ŠคํŒ…


์˜์‚ฌ๊ฒฐ์ •๋‚˜๋ฌด์—์„œ์˜ ๋ณ€์ˆ˜์ค‘์š”๋„

- ํ•ด๋‹น input ๋ณ€์ˆ˜๊ฐ€ ์˜์‚ฌ๊ฒฐ์ •๋‚˜๋ฌด์˜ ๊ตฌ์ถ•์—์„œ ์–ผ๋งˆ๋‚˜ ๋งŽ์ด ์“ฐ์ด๋‚˜ 
- ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ถ„๊ธฐ๋ฅผ ํ–ˆ์„ ๋•Œ ๊ฐ ๊ตฌ๊ฐ„์˜ ๋ณต์žก๋„๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ค„์–ด๋“œ๋Š”๊ฐ€? 



shapley ๊ฐ’ 

: ๊ฐ ๋ณ€์ˆ˜๊ฐ€ ์˜ˆ์ธก ๊ฒฐ๊ณผ๋ฌผ์— ์ฃผ๋Š” ์˜ํ–ฅ๋ ฅ์˜ ํฌ๊ธฐ

: ํ•ด๋‹น ๋ณ€์ˆ˜๊ฐ€ ์–ด๋–ค ์˜ํ–ฅ์„ ์ฃผ๋Š”๊ฐ€ 

 

(์˜ˆ) ์ถ•๊ตฌ ์„ ์ˆ˜ A , ์†ํ•œ ํŒ€ B 

- ๊ฐ ์„ ์ˆ˜๊ฐ€ ํŒ€ ์„ฑ์ ์— ์ฃผ๋Š” ์˜ํ–ฅ๋ ฅ ํฌํ‚ค

- ํ•ด๋‹น ์„ ์ˆ˜๊ฐ€ ์–ด๋– ํ•œ ์˜ํ–ฅ์„ ์ฃผ๋Š”๊ฐ€ 

- (์„ ์ˆ˜ A๊ฐ€ ์žˆ๋Š” ํŒ€ B์˜ ์Šน๋ฅ ) - (์„ ์ˆ˜ A๊ฐ€ ์—†๋Š” ํŒ€ B์˜ ์Šน๋ฅ  = 7% 


 shap value ์‹ค์Šต 

shap value ์‹ค์Šต์— ์ค‘์ ์„ ๋‘๊ธฐ ์œ„ํ•ด  Xgboost ํ•™์Šต๊นŒ์ง€ ์ „์— ํ–ˆ๋˜ ๊ทธ๋Œ€๋กœ ์‹คํ–‰ํ•ด์ค€๋‹ค. 

๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

import os
import pandas as pd
import numpy as np
os.chdir('./data') # ๋ณธ์ธ ๊ฒฝ๋กœ 
data = pd.read_csv("bank-additional-full.csv", sep = ";")

์ด์ „ ๊ธ€์—์„œ ์‚ฌ์šฉํ•˜์˜€๋˜ ์˜ˆ๊ธˆ ๊ฐ€์ž… ์—ฌ๋ถ€ ๋ฐ์ดํ„ฐ์…‹์ด๋‹ค. 

data = pd.get_dummies(data, columns = ['job','marital','education','default','housing','loan','contact','month','day_of_week','poutcome'])

๋ฒ”์ฃผํ˜• ๋ณ€์ˆ˜๋ฅผ get_dummies๋ฅผ ์ด์šฉํ•˜์—ฌ ์›ํ•ซ์ธ์ฝ”๋”ฉ ํ•ด์ค€๋‹ค. 

data['y'].value_counts()

๋ถ„๋ฅ˜ ๋ชจ๋ธ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ชฉํ‘œ๋ณ€์ˆ˜๋„ ๋‹น์—ฐํžˆ ๋ฒ”์ฃผํ˜• ๋ณ€์ˆ˜๋กœ ๋˜์–ด์žˆ๋‹ค. 

data['y'] = np.where( data['y'] == 'no', 0, 1)

ํ•˜์ง€๋งŒ shap value ํŒจํ‚ค์ง€๋Š” ๋ชฉํ‘œ๋ณ€์ˆ˜๊ฐ€ ์ˆ˜์น˜ํ˜•์ด์–ด์•ผ ์ž˜ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์น˜ํ™” ์‹œ์ผœ์ค€๋‹ค. 

 

 

 

Xgboost ํ•™์Šต 

input_var = ['age', 'duration', 'campaign', 'pdays', 'previous', 'emp.var.rate',
       'cons.price.idx', 'cons.conf.idx', 'euribor3m', 'nr.employed',
       'job_admin.', 'job_blue-collar', 'job_entrepreneur', 'job_housemaid',
       'job_management', 'job_retired', 'job_self-employed', 'job_services',
       'job_student', 'job_technician', 'job_unemployed', 'job_unknown',
       'marital_divorced', 'marital_married', 'marital_single',
       'marital_unknown', 'education_basic.4y', 'education_basic.6y',
       'education_basic.9y', 'education_high.school', 'education_illiterate',
       'education_professional.course', 'education_university.degree',
       'education_unknown', 'default_no', 'default_unknown', 'default_yes',
       'housing_no', 'housing_unknown', 'housing_yes', 'loan_no',
       'loan_unknown', 'loan_yes', 'contact_cellular', 'contact_telephone',
       'month_apr', 'month_aug', 'month_dec', 'month_jul', 'month_jun',
       'month_mar', 'month_may', 'month_nov', 'month_oct', 'month_sep',
       'day_of_week_fri', 'day_of_week_mon', 'day_of_week_thu',
       'day_of_week_tue', 'day_of_week_wed', 'poutcome_failure',
       'poutcome_nonexistent', 'poutcome_success']

y ์ปฌ๋Ÿผ์„ ์ œ์™ธํ•œ ์ธํ’‹๋ณ€์ˆ˜๋ฅผ ๋ฆฌ์ŠคํŠธ์— ๋ชจ๋‘ ๋‹ด์•„์ค€๋‹ค. 

from xgboost import XGBRegressor

์ˆ˜์น˜ํ˜•์œผ๋กœ ์˜ˆ์ธก์„ ์ง„ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด XBGRegressor ํšŒ๊ท€๋ชจ๋ธ์„ ์ž„ํฌํŠธ ํ•ด์ค€๋‹ค. 

xgb = XGBRegressor( n_estimators = 300, learning_rate=0.1 )
xgb.fit(data[input_var], data['y'])

Xgboost ํ•™์Šต์„ ์ง„ํ–‰ํ•œ๋‹ค. 

 

 

Shap Value ์˜ˆ์ œ 

import shap

shap ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ import ํ•ด์ค€๋‹ค. 

 

(1) ๋ณ€์ˆ˜์ค‘์š”๋„

explainer = shap.TreeExplainer(xgb)
shap_values = explainer.shap_values( data[input_var] )

shap.TreeExplainer์˜ ์ธ์ž์— ํ•™์Šตํ•œ ๋ชจ๋ธ xgb๋ฅผ ๋„ฃ์–ด ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•ด์ค€๋‹ค. ๊ทธ๋‹ค์Œ explainer.shap_values์˜ ์ธ์ž์— ๋ฐ์ดํ„ฐ์…‹์˜ ์ธํ’‹๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค. 

shap.summary_plot( shap_values , data[input_var] , plot_type="bar" )

shap.summary_plot์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€์ˆ˜์ค‘์š”๋„ ๊ทธ๋ž˜ํ”„๋ฅผ ๊ทธ๋ ค์ค€๋‹ค. ๊ฐ€์žฅ ๋†’์€ ๋ณ€์ˆ˜๋Š” duration์ด๋‹ค. duration์€ ์ „ํ™”์‹œ๊ฐ„์ด๋‹ค. ์ „ํ™”์‹œ๊ฐ„์˜ ๊ธธ์ด๊ฐ€ ์ด ๋ชจ๋ธ์˜ ์˜ˆ์ธก์— ๊ฐ€์žฅ ์˜ํ–ฅ์„ ๋งŽ์ด ๋ฏธ์นœ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. 

 

 

(2) dependence plot 

: ํŠน์ • input ๋ณ€์ˆ˜์™€ target ๋ณ€์ˆ˜์™€์˜ ๊ด€๊ณ„๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ 

: ์ ์€ ๊ฐ๊ฐ์˜ row๋ฅผ ์˜๋ฏธ(๋ฐ์ดํ„ฐ ํ•œ๊ฐœ), ํƒ€๊ฒŸ๋ณ€์ˆ˜์— ๋ฏธ์นœ ์˜ํ–ฅ = y 

: ํ•ด๋‹น ๋ณ€์ˆ˜๊ฐ€ ์–ด๋–ป๊ฒŒ ์˜ํ–ฅ์„ ๋ฏธ์ณค๋Š”์ง€ ์„ฌ์„ธํ•˜๊ฒŒ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

shap.dependence_plot( 'duration' , shap_values , data[input_var] )

duration์˜ ๊ทธ๋ž˜ํ”„๋ฅผ ๋ณด๋ฉด duration์˜ ๋Œ€๋ถ€๋ถ„์ด 3000 ๋ฏธ๋งŒ์— ์กด์žฌํ•˜๊ณ , ๊ทธ ์ค‘์—์„œ๋Š” duration์ด 50์ด์ƒ์ฏค ๋˜๋ฉด ์ข‹์€ ์˜ํ–ฅ๋ ฅ์„ ๋ผ์ณ 1์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง„๋‹ค๊ณ  ํ•ด์„๋œ๋‹ค. (shpa value for duration์ด 0๋ณด๋‹ค ํฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์Œ) 

shap.dependence_plot( 'nr.employed' , shap_values , data[input_var] )

5020์ฏค ๋˜๋Š” ์ง€์ ์—์„œ ์˜ํ–ฅ๋ ฅ์ด ์Œ์ˆ˜๊ฐ€ ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  5100์ด ๋„˜์–ด๊ฐ€๊ณ ๋Š” ์Œ์ˆ˜์˜ ์˜ํ–ฅ๋ ฅ๋ฐ–์— ์—†๋‹ค. (-> 0์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Œ) ๊ทธ ์ด์ „์—๋Š” ์˜ํ–ฅ๋ ฅ์ด ๋†’์œผ๋ฏ€๋กœ ์ข‹์€ ์˜ํ–ฅ๋ ฅ์„ ๋ผ์นœ๋‹ค. (-> 1์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Œ) 

shap.dependence_plot( 'euribor3m' , shap_values , data[input_var] )

์Œ์ˆ˜์™€ ์–‘์ˆ˜๊ฐ€ ๋น„์Šทํ•˜๊ฒŒ ๋ถ„ํฌ๋˜์–ด์žˆ๋Š” ๊ฒƒ ๊ฐ™์•„ ๋ณด์ธ๋‹ค. ์ด ์ค‘์—์„œ ์Œ์ˆ˜๊ฐ€ ์–ผ๋งˆ ์—†๊ณ  ์–‘์ˆ˜๊ฐ€ ๋งŽ์€ ๊ตฌ๊ฐ„์„ ์ฐพ์•„๋ณด๋ฉด 1.3~1.4 - 2, 4-5 ๊ฐ€ ์žˆ๋‹ค. ํ•ด๋‹น ๊ตฌ๊ฐ„์ผ ๋•Œ 1์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค๊ณ  ํ•ด์„ํ•  ์ˆ˜ ์žˆ๋‹ค. 

shap.dependence_plot( 'cons.conf.idx' , shap_values , data[input_var] )

์ „์ฒด์ ์œผ๋กœ ์Œ์ˆ˜๋ฅผ ์ด๋ฃจ๊ณ  ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. -45์ดํ•˜์ผ ๋•Œ๋Š” 1์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง„๋‹ค๊ณ  ํ•ด์„ํ•  ์ˆ˜ ์žˆ๋‹ค. 

shap.dependence_plot( 'pdays' , shap_values , data[input_var] )

pdays๊ฐ€ 0์ผ๋•Œ ๋Œ€๋‹ค์ˆ˜์˜ ๋ฐ์ดํ„ฐ๊ฐ€ 1์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์งˆ ๊ฒƒ์ด๋ผ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

 

 

(3) force plot

: ํŠน์ • ๊ฐ’์ด ์–ด๋–ป๊ฒŒ ์˜ˆ์ธก๋˜์—ˆ๋Š”์ง€๋ฅผ ์‹œ๊ฐํ™” 

prediction = xgb.predict(data[input_var])
data['pred'] = prediction

 

shap.initjs()
shap.force_plot( explainer.expected_value , shap_values[41187] , data[input_var].iloc[41187] )

411187๋ฒˆ์งธ ๋ฐ์ดํ„ฐ๋Š” 0.09๊ฐ€ ๋‚˜์™”๋Š”๋ฐ, ๋–จ์–ด๋œจ๋ฆฌ๋Š” ๋ณ€์ˆ˜์™€ ์˜ฌ๋ฆฌ๋Š” ๋ณ€์ˆ˜๊ฐ€ ๊ณจ๊ณ ๋ฃจ ๋ถ„ํฌ๋˜์–ด ์žˆ๋‹ค. 

 

shap.force_plot( explainer.expected_value , shap_values[0] , data[input_var].iloc[41187] )

0์— ๊ฑฐ์˜ ๊ฐ€๊น๊ฒŒ ์˜ˆ์ธก๋œ 0๋ฒˆ์งธ ๋ฐ์ดํ„ฐ๋Š” ๊ฑฐ์˜ ๋ชจ๋“  ๋ณ€์ˆ˜๊ฐ€ ์Œ์ˆ˜์˜ ์˜ํ–ฅ๋ ฅ์„ ๋ผ์นœ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

 

41183๋ฒˆ์งธ ๋ฐ์ดํ„ฐ๋Š” ์–‘์˜ ์˜ํ–ฅ๋ ฅ์ด ํ›จ์”ฌ ๋†’์€ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ 0.88์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”๊ณ , ์ •๋‹ต์€ 1๋กœ, ๊ทผ์ ‘ํ•˜๊ฒŒ ๋งžํ˜”๋‹ค. 

 

 

์ด๋ ‡๊ฒŒ shap ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ๋ณ€์ˆ˜๊ฐ€ ์˜ˆ์ธก์— ์–ด๋– ํ•œ ์˜ํ–ฅ์„ ๋ฏธ์ณค๋Š”์ง€ ์„ฌ์„ธํ•˜๊ฒŒ ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.  


 

 

 

 

 

 

 

 

 

 

 

 

 

 

+ Recent posts