본문 바로가기
기계학습(ML)/Interpreting Model

n234 Interpreting ML Model( X-AI )

by kiimy 2021. 6. 26.
728x90
  • 부분의존도그림(Partial dependence plot, PDP)을 시각화하고 해석할 수 있습니다.
  • 개별 예측 사례를 Shap value plots을 사용해 설명할 수 있습니다.
  • black box model이란?

* X-AI

- 모델이 어떻게 이런 예측을 했는지를 확인하기 위한 ( 예측모델 설명 )

*Partial Dependence Plots( PDP )

- ICE곡선들의 평균(= ICE 곡선은 하나의 관측치에 대해 관심 특성을 변화 시킴에 따른 타겟값 변화 곡선 )

  • 복잡한 모델 -> 이해하기 어렵지만 성능이 좋다. ( 특성중요도가 긍정인지 부정인지 파악 어려움 )
  • ==> so, PDP사용 = feature 가 target의 어떻게 영향을 미치는지 파악 가능
  • 단순한 모델 -> 이해하기 쉽지만 성능이 부족하다. (ex 회귀계수 양의선형관계 / 음의선형관계 )

* PDP 1(= 1개의 특성과 타겟과의 관계 )

encoder = OrdinalEncoder()
X_train_encoded = encoder.fit_transform(X_train) # 학습데이터
X_val_encoded = encoder.transform(X_val) # 검증데이터

boosting = XGBRegressor(
    n_estimators=1000,
    objective='reg:squarederror', # default
    learning_rate=0.2,
    n_jobs=-1
)

eval_set = [(X_train_encoded, y_train), 
            (X_val_encoded, y_val)]

boosting.fit(X_train_encoded, y_train, 
          eval_set=eval_set,
          early_stopping_rounds=50
         )


from pdpbox.pdp import pdp_isolate, pdp_plot

feature = 'annual_inc'

isolated = pdp_isolate(
    model=linear, # or boosting / model변수 지정에 따라 그래프 다름
    dataset=X_val, # X_val_encoded = boosting은 인코딩함 
    model_features=X_val.columns, # X_val_encoded.columns
    feature=feature,
    grid_type='percentile', # default='percentile', or 'equal'
    num_grid_points=10 # default=10
)
pdp_plot(isolated, feature_name=feature);

########################  ICE curve ##################
pdp_plot(isolated
         , feature_name=feature
         , plot_lines=True # ICE plots
         , frac_to_plot=0.001 # or 10 (# 10000 val set * 0.001)
         , plot_pts_dist=True) 


* PDP 2 (= 두 특성간의 상호작용 )

from pdpbox.pdp import pdp_interact, pdp_interact_plot

features = ['annual_inc', 'fico_range_high']

interaction = pdp_interact(
    model=boosting, 
    dataset=X_val_encoded,
    model_features=X_val_encoded.columns, 
    features=features
)


pdp_interact_plot(interaction, plot_type='grid', 
                  feature_names=features);
                 

* Encoding 전 특성의 범주이름으로 확인하는법 ( PDP 1 )

#인코딩을 하게되면 학습 후 PDP 를 그릴 때 인코딩된 값이
#나오게 되어 카테고리특성의 실제 값을 확인하기 어려움
# 클래스가 male, female 경우 인코딩하면 1, 2 로 나옴

pipe = make_pipeline(
    OrdinalEncoder(), 
    RandomForestClassifier(n_estimators=100, random_state=42, n_jobs=-1)
)
pipe.fit(X, y);

encoder = pipe.named_steps['ordinalencoder']
X_encoded = encoder.fit_transform(X)
rf = pipe.named_steps['randomforestclassifier']

feature = 'sex'
pdp_dist = pdp.pdp_isolate(model=rf, dataset=X_encoded, model_features=features, feature=feature)
pdp.pdp_plot(pdp_dist, feature) # male과 female이 1과 2로된 그래프로 나옴

encoder.mapping # 하면 인코딩된 칼럼들이 나옴
'''
[{'col': 'sex',
  'mapping': male      1
  female    2
  NaN      -2
  dtype: int64,
  'data_type': dtype('O')},
 '''

# 추가하면 x축 male과 female로 바뀜
plt.xticks([1, 2], ['male', 'female',]); 

##################### or #############

feature = 'sex'
for item in encoder.mapping:
    if item['col'] == feature:
        feature_mapping = item['mapping'] # Series
        
feature_mapping = feature_mapping[feature_mapping.index.dropna()]
category_names = feature_mapping.index.tolist()
category_codes = feature_mapping.values.tolist()

pdp.pdp_plot(pdp_dist, feature)

# xticks labels 설정을 위한 리스트를 직접 넣지 않아도 됩니다 
plt.xticks(category_codes, category_names);

* PDP 2

features = ['sex', 'age']

interaction = pdp_interact(
    model=rf, 
    dataset=X_encoded, 
    model_features=X_encoded.columns, 
    features=features
)

pdp_interact_plot(interaction, plot_type='grid', feature_names=features);

# sex 특성이 인코딩되서 나옴


pdp = interaction.pdp.pivot_table(
    values='preds', 
    columns=features[0], 
    index=features[1]
)[::-1]

pdp = pdp.rename(columns=dict(zip(category_codes, category_names)))
plt.figure(figsize=(6,5))
sns.heatmap(pdp, annot=True, fmt='.2f', cmap='viridis')
plt.title('PDP decoded categorical');

*SHAP / gradCAM(= image)

어떤 머신러닝 모델이든지 단일 관측치로부터 특성들의 기여도(feature attribution)를 계산하기 위한 방법

모델을 모두 학습하고 평가한 후에 수행하는 "사후 분석" 방범 중 하나

- 그렇기 때문에 Shapley Value 결과만으로 어떤 변수로 인해 어떤 결과가 나왔다는 인과관계를 정립시킬 수 없다

(= 예측력에 주요하게 기여한 것은 맞지만 이를 일반화 시킬수는 없다.)

Shapley value 방법으로 계산된 설명은 항상 모든 특성을 사용한다. 사람들은 LIME과 같이 선택적인 설명을 선호한다. 비전문가가 사용하기에는 LIME이 더 적절한 설명 방법일 수 있다.

Shapley value는 LIME과 다르게 설명가능한 모델이 아닌 단순히 특성별 기여도를 나타내는 값이다. 즉 입력값의 변화에 따른 예측값의 변화를 설명하기 힘들다. 예를 들면 “내가 1년에 300 유로를 더 번다면 내 신용점수는 5 포인트만큼 오를거야”와 같은 설명이 불가능하다.
또 다른 단점으로는 새로운 데이터에 대해서 Shapley value를 계산할 때 생기는 문제점이다. 새로운 데이터에 대한 정보가 기존 데이터에 없게되면 이 값을 대체하기위한 진짜와 비슷한 가상의 데이터를 만들어야지만 이 문제를 해결할 수 있다.

https://techblog-history-younghunjo1.tistory.com/200

 

[ML] Explainable AI - Shapley Value

🔉해당 포스팅에서 사용된 자료는 고려대학교 산업경영공학부 김성범교수님의 Youtube 강의자료에 기반했음을 알려드립니다. 혹여나 출처를 밝혔음에도 불구하고 저작권의 문제가 된다면 joyh951

techblog-history-younghunjo1.tistory.com

https://datanetworkanalysis.github.io/2019/12/23/shap1

 

SHAP에 대한 모든 것 - part 1 : Shapley Values 알아보기

1. 게임이론 (Game Thoery) Shapley Value에 대해 알기위해서는 게임이론에 대해 먼저 이해해야한다. 게임이론이란 우리가 아는 게임을 말하는 것이 아닌 여러 주제가 서로 영향을 미치는 상황에서 서로

datanetworkanalysis.github.io

* Shapley values

게임이론에서 같은 팀 선수들(특성들)이 게임 목표(예측) 달성을 위해 각자 자신의 역할(기여)을 한다고 할 때 게임 목표 달성 후 받은 포상을 어떻게 하면 그들의 기여도에 따라 공평하게 나누어 줄 수 있을 것인가? 라는 질문과 연관됩니다.

==> 머신러닝의 특성 기여도(feature attribution) 산정에 활용

row = X_test.iloc[[1]]  # 중첩 brackets을 사용하면 결과물이 DataFrame입니다
row

# 실제 집값
y_test.iloc[[1]] # 2번째 데이터를 사용했습니다

# 모델 예측값
model.predict(row)

import shap

explainer = shap.TreeExplainer(model) # 최적의 파라미터값으로 구한 model
# model = search.best_estimator_

shap_values = explainer.shap_values(row) # 각 특성의 기여도

shap.initjs()
shap.force_plot(
    base_value=explainer.expected_value, 
    shap_values=shap_values,
    features=row
)

==> target 값에 bathroom은 값을 올리고 / lat, bedrooms 은 값을 내리는데 기여했다.

shap_values = explainer.shap_values(X_test.iloc[:300])
shap.summary_plot(shap_values, X_train.iloc[:300])

shap.summary_plot(shap_values, X_train.iloc[:300], plot_type="violin")

shap.summary_plot(shap_values, X_train.iloc[:300], plot_type="bar")
'''
==> pumutation 보다 더 정확하다고 할 수 있다.
    * pumutation features는 서로 다른 특성들간의 영향을 미치는 경우에는
    결과가 정확하지 않을 수 있다
    
    * 음의 관계에 대해서는 계산하지 않는다.
'''

오른쪽 Feature Value는 말그대로 Feature 자체 값을 뜻함(= 변수 중요도가 아님), 중요도 값은 X축 SHAP value임)

= 하나의 관측치마다의 전체 변수의 중요도

= 그래프 상에서 특성은 예측에 미치는 영향력(=중요도)에 따라 정렬

= 현재 그래프로는 특성 값이 예측하는데 어떻게 영향을 준다고 판단하기는 어려움(ex. 작은값이 긍정, 큰 값이 부정과 같이 나눠져야 해석 가능)

= 전체 관측치에 대해서 전체 변수의 전박적인 중요도 값

shap_interaction_values = explainer.shap_interaction_values(X_train)
shap.summary_plot(shap_interaction_values, X_train)

interaction plot

- 각 특성 간의 관계(=상호작용 효과)를 파악

- 한 특성이 모델에 미치는 영향도에는 각 특성 간의 관계도 포함될 수 있어 이를 따로 분리함으로써 추가적인 인사이트 확인 가능

shap.dependence_plot(
    ('RM', 'LSTAT'),
    shap_interaction_values, X_train,
    display_features=X_train
)

 

RM = 7.5? 이상의 값에서 예측값(=LSTAT)에 긍정적인 영향을 미치고 있다

# 클래스의 비율
y_train.value_counts(normalize=True)
'''
Fully Paid     0.847328
Charged Off    0.152672
Name: loan_status, dtype: float64
'''

ratio = 0.15/0.84
ratio

model = XGBClassifier(n_estimators=1000, verbosity=0, 
                        n_jobs=-1, 
                        scale_pos_weight=ratio)

서로 관련이 있는 모든 특성들에 대한 전역적인(Global) 설명

  • Feature Importances
  • Drop-Column Importances
  • Permutaton Importances

타겟과 관련이 있는 개별 특성들에 대한 전역적인 설명

  • Partial Dependence plots

개별 관측치에 대한 지역적인(local) 설명

  • Shapley Values
728x90

댓글