SHAP
SHapley Additive exPlanations
게임 이론 기반 모델 해석 기법. 각 피처가 예측에 기여한 정도를 정량화. XAI(설명 가능한 AI)의 핵심 도구.
SHapley Additive exPlanations
게임 이론 기반 모델 해석 기법. 각 피처가 예측에 기여한 정도를 정량화. XAI(설명 가능한 AI)의 핵심 도구.
SHAP(SHapley Additive exPlanations)는 게임 이론의 Shapley Value를 머신러닝에 적용한 모델 해석 기법입니다. 각 피처가 개별 예측에 얼마나 기여했는지 수치로 정량화하여 "왜 이 예측이 나왔는지"를 설명합니다. 예를 들어 대출 거절 시 "소득이 -0.3, 신용점수가 -0.25 기여했다"처럼 정량적 근거를 제시할 수 있습니다.
2017년 워싱턴 대학의 Scott Lundberg가 NIPS 논문에서 발표했고, 현재 XAI(Explainable AI) 분야에서 가장 널리 사용되는 기법입니다. LIME(Local Interpretable Model-agnostic Explanations)과 달리 이론적 기반이 탄탄하고, 일관성(Consistency), 지역 정확성(Local Accuracy), 효율성(Efficiency), 대칭성(Symmetry) 등 Shapley Value의 공리적 성질을 상속받습니다.
핵심 아이디어는 각 피처를 "플레이어"로, 예측 결과를 "보상"으로 보고, 각 플레이어가 팀(모델)에 기여한 정도를 게임 이론적으로 공정하게 배분하는 것입니다. 수학적으로 SHAP 값은 모든 가능한 피처 부분집합(2^n개)에 대한 marginal contribution의 가중 평균입니다. 계산 비용이 지수적이지만, TreeSHAP(트리 기반 모델, O(TL^2)), DeepSHAP(딥러닝), KernelSHAP(모델 불가지론) 등 효율적인 근사 알고리즘이 있습니다.
금융, 의료, 법률, 보험 등 규제 산업에서 모델 설명 의무가 있을 때 필수로 사용됩니다. EU GDPR의 "설명 받을 권리"(Right to Explanation), 미국 FCRA/ECOA의 대출 거절 사유 고지 의무, 금융감독원의 AI 모델 검증 가이드라인 등에 대응할 수 있습니다. 또한 모델 디버깅, 피처 엔지니어링 검증, 데이터 드리프트 탐지에도 활용됩니다. 최근에는 LLM의 토큰 수준 기여도 분석에도 SHAP이 확장 적용되고 있습니다.
import shap
import xgboost as xgb
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
# 데이터 로드 및 분할
data = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
data.data, data.target, test_size=0.2, random_state=42
)
# XGBoost 모델 학습
model = xgb.XGBClassifier(n_estimators=100, max_depth=4)
model.fit(X_train, y_train)
# SHAP Explainer 생성 (TreeSHAP 사용)
explainer = shap.TreeExplainer(model)
# 테스트 데이터에 대한 SHAP 값 계산
shap_values = explainer.shap_values(X_test)
# 전체 피처 중요도 시각화 (Summary Plot)
shap.summary_plot(shap_values, X_test, feature_names=data.feature_names)
# 개별 예측 설명 (Force Plot)
shap.force_plot(
explainer.expected_value,
shap_values[0],
X_test[0],
feature_names=data.feature_names
)
# Waterfall Plot (단일 샘플 상세 분석)
shap.waterfall_plot(
shap.Explanation(
values=shap_values[0],
base_values=explainer.expected_value,
data=X_test[0],
feature_names=data.feature_names
)
)
# SHAP 값 해석:
# - 양수: 해당 피처가 예측을 높이는 방향으로 기여
# - 음수: 해당 피처가 예측을 낮추는 방향으로 기여
# - 절대값: 기여의 크기
# === DeepSHAP: 딥러닝 모델 설명 ===
import tensorflow as tf
# 딥러닝 모델 생성
deep_model = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation='relu', input_shape=(30,)),
tf.keras.layers.Dense(32, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
deep_model.compile(optimizer='adam', loss='binary_crossentropy')
deep_model.fit(X_train, y_train, epochs=10, verbose=0)
# DeepExplainer 사용
deep_explainer = shap.DeepExplainer(deep_model, X_train[:100])
deep_shap_values = deep_explainer.shap_values(X_test[:10])
# === KernelSHAP: 모델 불가지론 (어떤 모델이든 적용) ===
def model_predict(X):
"""블랙박스 모델의 예측 함수"""
return model.predict_proba(X)[:, 1]
kernel_explainer = shap.KernelExplainer(model_predict, shap.sample(X_train, 100))
kernel_shap_values = kernel_explainer.shap_values(X_test[:5], nsamples=500)
# === 전역 피처 중요도 (Global Feature Importance) ===
# 모든 샘플에 대한 SHAP 값의 절대값 평균
global_importance = np.abs(shap_values).mean(axis=0)
feature_ranking = sorted(
zip(data.feature_names, global_importance),
key=lambda x: x[1], reverse=True
)
print("전역 피처 중요도 Top 5:")
for name, importance in feature_ranking[:5]:
print(f" {name}: {importance:.4f}")
# === Dependence Plot: 피처 간 상호작용 분석 ===
shap.dependence_plot(
"mean radius",
shap_values, X_test,
feature_names=data.feature_names,
interaction_index="mean texture" # 상호작용 피처
)
# === Force Plot: 개별 예측 설명 ===
# 단일 샘플의 SHAP 값을 막대 그래프로 표시
shap.force_plot(
explainer.expected_value,
shap_values[0],
X_test[0],
feature_names=data.feature_names,
matplotlib=True
)
# === Waterfall Plot: 기준값에서 예측까지 ===
# 각 피처가 예측을 어떻게 밀어올리고 내리는지 시각화
shap.waterfall_plot(
shap.Explanation(
values=shap_values[0],
base_values=explainer.expected_value,
data=X_test[0],
feature_names=data.feature_names
)
)
# === Batch 처리 및 저장 ===
# 대규모 데이터셋에서 효율적으로 SHAP 계산
def compute_shap_batch(model, X, batch_size=100):
"""배치 단위로 SHAP 값 계산"""
all_shap_values = []
explainer = shap.TreeExplainer(model)
for i in range(0, len(X), batch_size):
batch = X[i:i+batch_size]
shap_values = explainer.shap_values(batch)
all_shap_values.append(shap_values)
return np.concatenate(all_shap_values, axis=0)
# SHAP 값 저장 및 로드
import pickle
with open('shap_values.pkl', 'wb') as f:
pickle.dump(shap_values, f)
# === LLM/Transformer 모델 SHAP ===
# Transformer 기반 모델에도 SHAP 적용 가능
# PartitionExplainer: 토큰 수준 기여도 분석
import shap
def explain_text_classification(text, model, tokenizer):
"""텍스트 분류 모델의 SHAP 분석"""
def predict_fn(texts):
inputs = tokenizer(texts, return_tensors='pt', padding=True)
outputs = model(**inputs)
return outputs.logits.softmax(dim=-1).detach().numpy()
explainer = shap.Explainer(predict_fn, tokenizer)
shap_values = explainer([text])
return shap_values
# SHAP은 모델 디버깅과 규제 대응의 필수 도구입니다
"금융감독원 검사 대응하려면 SHAP 리포트 필수예요. 대출 거절 사유를 '신용점수가 낮아서'가 아니라 'SHAP 분석 결과 신용점수가 -0.35, 연체 이력이 -0.28 기여'처럼 정량적으로 제시해야 합니다."
"SHAP은 게임 이론의 Shapley Value를 ML에 적용한 기법입니다. 각 피처의 기여도를 모든 가능한 피처 조합에 대해 평균하므로 이론적으로 공정합니다. TreeSHAP을 쓰면 트리 기반 모델에서 O(TL^2)로 효율적인 계산이 가능해요."
"모델이 이상한 예측 내는 케이스 있으면 SHAP waterfall 플롯 보세요. 어떤 피처가 예측을 얼마나 밀어올렸는지 바로 보여요. 데이터 이상치인지, 피처 엔지니어링 문제인지 바로 구분됩니다."
Exact SHAP은 피처 수에 지수적으로 증가합니다. 100개 피처면 계산 불가능. TreeSHAP, KernelSHAP 등 근사 알고리즘을 사용하세요.
높은 상관관계를 가진 피처들은 SHAP 값이 분산됩니다. 피처 A와 B가 0.95 상관이면 기여도가 둘 사이에 나뉘어 개별 해석이 어렵습니다.
background 데이터를 대표성 있게 샘플링하고, 피처 상관관계를 먼저 분석하세요. 규제 대응 시 SHAP 값과 함께 피처 정의, 데이터 범위도 문서화합니다.