티스토리 뷰

반응형

비즈니스 해석에 대한 중요한 글을 찾아 더 디벨롭해서 정리해보고자 한다.

아마 이번 기수에서는 배우지 않을 것 같아 쓴다.

 

모델을 만드는 데 있어 항상 필요한 두 가지의 질문.

1. 모델이 왜 그렇게 예측했는가?

2. 그 모델이 정말로 비즈니스 문제를 해결 할 수 있는가?

 

위의 두 문제에 대한 걸 알아보자.

 

interpretability(해석 가능성) 과 Explainability(설명 가능성)

 

왜 모델이 그렇게 예측했는지에 대한 문제

 

본질적으로 해석 가능한 모델 -> whitebox model

즉, 시각화가 가능하거나(Decision Tree 기반) coef 도출이 가능한 선형 회귀 문제 등이다.

Output을 본질적으로 해석할 수 없다면(딥러닝) Blackbox model이 된다.

 

Explainablity는 해석을 포함한 개념이다. 모델이 학습한 과정을 단계적으로 설명할 수 있어야 한다는 개념.

 

둘은 Trade -off 관계를 가진다.

설명이 잘되는 알고리즘 -> 성능이 낮다.(lr, knn)

성능이 좋은 알고리즘 -> 설명이 어렵다 (catboost,rf)

 

하지만, 중요한 건, 모델에 대한 설명은 모델의 성능이 일단 좋아야 의미가 있다.

그렇기에 살펴볼 것은 3가지이다.

 

1. 해당 모델에서 어떤 피처가 제일 중요한가 = feature importance

2. 특정 feature 값이 변했을 때, 예측값의 변화 = partial Dependent Plot (PDP)

3. 이 데이터는 왜 이렇게 예측되었는지 = Shapley Additive Explanation (SHAP)

 

Feature importance

알고리즘 별로 계산되어, 각 특징이 모델에 미치는 중요도를 수치화한 값.

Tree 기반 알고리즘(RF, lgbm, xgboost, catboost) 등은 자체적으로 제공한다.

 

트리 기반 알고리즘의 경우, 지니 불순도가 감소하는 정도(information gain)가중 평균을 각 feature 별로 계산한다.

지니 불순도 : 최대한 덜 섞일수록 좋다. 즉, 순도가 높으면 분류가 잘 된 것.(gini값으로는 불순도이므로 0 - 완벽 분류)

1-(2/10)^2 -(8/10)^2 = 0.32

부모의 불순도에서 자녀의 불순도를 뺀 것이 정보 이득(information gain).

즉, 정보 이득이 높을수록 아주 좋다.

 

트리에서

분류문제의 경우 불순도. 이진 분류의 경우 0~0.5 사이의 값이다.

회귀문제의 경우 MSE

 

분기하는 노드의 sample 수가 가중치이다.

이걸 MDI(Mean Decrease Impurity) 라고 한다. 

정보이득을 많이 얻은 feature일수록 MDI가 높다.

 

Mean Decrease GINI는, RF 모델에서 사용하는 변수 중요도이다.

estimators로 참여하는 각 트리의 MDI 평균을 도출한다.

 

부스팅 계열

부스팅 계열은 plot_importance를 사용한다.

트리가 분리될 때의 기준으로 사용된 횟수의 합으로 중요도를 표시한다.

 

gain은 model.feature_importances_에서 나오는 값.

매개변수로 total_gain을 준다면 총합을 사용.

 

cover은 트리가 분기될 떄의 샘플 수 평균이다. 트리의 깊이가 얕으면 자주 선택된 기준일 수록 높은 값.

 

PFI(Permutation Feature importance)

전체 학습데이터에서 하나의 피처를 선택해 무작위로 섞었을 때 model의 성능이 얼마나 감소되는지를 계산.

랜덤 요소 때문에 성능감소를 계산하는 과정을 n번 반복하여 평균을 낸다.

 

PFI = s(원래 모델 점수) - (1/n) * 시그마 (sn)

 

여기서 sn은 해당 피처를 섞었을 때의 성능 점수다.

PFI가 클수록 해당 변수가 중요하다는 의미(해당 피처가 없을 때 모델의 성능이 감소하는 거라고 생각하면 된다.)

 

단점 중 하나는 요인간의 공선성이 존재했을 때 모델의 성능에 큰 변화가 없을 수 있어 PFI를 정상적으로 구할 수 없다.

 

from sklearn.inspection import permutation_importance

pfi = permutation_importance(model, x_test, y_test, n_repeats=50, scoring='r2')

pfi 값을 딕셔너리로 반환한다.

딥러닝을 포함하여 모델과 test 데이터만 있으면 모든 모델에 적용 가능.

 

SHAP (SHapley Additive exPlanation)

SHAP는 PFI와 달리 모델이 예측한 값 하나하나에 대해서 변수가 예측값에 얼마나 기여했는지를 나타내는 수치.

 

간단한 원리는 가중치 * 기여도 계산

 

1. 구하려고 하는 변수 k 설정

2.  k를 제외한 나머지 변수들로 가능한 모든 조합 구성(2^(n-1)개)

3. 각 조합별로 k를 포함하여 계산한 예측값을 추출

4. 그 후 각 조합별로 k 빼고 예측값을 추출

5. 4-3을 하고, 가중치를 곱하여 가중치 평균 추출.

 

간단하게 말하면, k를 포함한 모델의 예측값과, k를 뺀 모델의 예측값에 가중치를 곱하는 것이다.

그걸 전부 모아서 평균을 내는 것.

 

SHAP 라이브러리

각 알고리즘 별로 맞는 함수 사용

Tree 기반은 TreeExplainer

딥러닝은 DeepExplainer

SVM은 kernelExplainer

그 외 알고리즘은 Explainer.

다만, sklearn, keras 기반 모델 외 사용시에는 정확하게 동작하지 않을 수 있음을 유의

 

반환되는 값 : shape 데이터, 실제 데이터 값 대신 기여햔 정도가 저장

 

그래프 그리기

force_plot - 개별 데이터 시각화

summary_plot - 요약 데이터 시각화

dependence_plot - 하나의 변수와 SHAP 값 간의 관계를 시각화

 

비즈니스 관점의 모델 평가

비즈니스 관점에서의 모델 평가는 항상 수익성을 통해 이루어져야 한다.

 

기술 지표(MSE, F1 score 등)는 개발자들 간의 대화는 가능하나, 비즈니스 문제를 말하기엔 부적합

 

비즈니스 지표는 매출액, 이익, 비용, 회전율 등이 있다.

 

분류 문제에서의 비즈니스 평가

이진 confusion matrix에서 이익 또는 손해를 수치화.

 

실제 P인데 P 예측 : 이자 수익 0.5

실제 P 인데 N이라 예측했다면 손실 : -0.5

실제 N인데 P라 예측 했다면 연체로 손실 : -0.1

실제 N인데 N이라 예측했다면 이득 : 0.3

 

여기에 원하는 가중치를 곱하면 된다.

1000만원을 곱한다면 => 500만원, -500만원, -100만원, 300만원.

이런 식.

 

회귀 문제에서의 비즈니스 평가

예측값 별로 후속 프로세스를 시뮬레이션을 통해 어떤 수익과 비용이 발생하는지 따져야 한다.

 

시뮬레이션 수행: 예측값 범위를 여러 구간으로 나누고, 각 구간별로 다음과 같이 진행.
1. 해당 구간의 예측값을 갖는 데이터 샘플을 선택한다.
2. 선택된 샘플들에 대해 SHAP 값을 참조하여 예측값에 영향을 미치는 주요 특성들을 파악한다.
3. 주요 특성들의 값을 변화시키며 후속 프로세스를 시뮬레이션하고, 발생하는 수익과 비용을 계산한다.
4 . 각 구간별로 계산된 수익과 비용을 종합하여 최적의 예측값 범위와 해당 범위에서의 의사결정 전략을 도출.

 

PFI 코드

from sklearn.inspection import permutation_importance
from catboost import CatBoostRegressor

model = CatBoostRegressor(max_depth = 10)
model.fit(x_train, y_train)

pfi = permutation_importance(model, x_test, y_test, n_repeats=50, scoring='neg_mean_absolute_percentage_error')

#시각화
# 아래에 필요한 코드를 작성하고 결과를 확인합니다.
pfi = pfi['importances_mean']/ np.sum( pfi['importances_mean'])
# # Feature 중요도 확인
perf_dic = {'feature':list(x_train),
            'importance': pfi}
df = pd.DataFrame(perf_dic)
df.sort_values(by='importance', ascending=True, inplace=True)

# 시각화
plt.figure(figsize=(6, 6))
plt.barh(df['feature'], df['importance'])
plt.tight_layout()
plt.show()

SHAP 코드

force plot : 개별 데이터 샘플에 대한 SHAP 값을 시각화

import shap
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor #사용할 모델

# 데이터와 모델 준비
X_train, X_val, y_train, y_val = ... 

model = RandomForestRegressor().fit(X_train, y_train)

# SHAP 값 계산
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_val)

# 특정 데이터 샘플 선택 
sample_idx = 0 
shap_sample = shap_values[sample_idx]
data_sample = x_test.iloc[sample_idx]

shap.force_plot(shap_sample, data_sample, feature_names=X_val.columns)

plt.show()

shap.initjs() 

# force_plot(전체평균, shapley_values, input)
shap.force_plot(explainer.expected_value, shap_values[0, :], x_test.iloc[0,:])

데이터의 예측값 = 30.89, 평균값도 30.89

예측값 증가요인 = 7_day, 그리고 wait_time

 

SHAP 값이 양수이면 그 피처가 모델의 예측 결과를 높이는 데 기여했다는 뜻이고, 반대로 음수이면 예측 결과를 낮추는 데 기여했다는 의미

 

summary_plot : 전체 데이터에 대한 SHAP 값 요약을 시각화

shap.summary_plot(shap_values, x_val_df)

#혹은
# summary_plot 시각화
shap.summary_plot(shap_values, X_val_df, feature_names=X_val_df.columns)
plt.show()

 

 

빨간 부분으로 증가할수록 예측값이 증가하는 요인이 된다.

즉, ride_cnt는 양의 양향으로 증가할수록 예측값 증가, 음의 방향으로 증가할수록 감소.

그리고 weekday_saturday는 음의 방향으로 증가할수록 예측값 증가, 양의 방향으로 증가할수록 감소가 된다.

 

 

dependence_plot: 특정 특성과 SHAP 값 간의 관계를 시각화

# 특성 선택
feature_idx = 2  # 시각화할 특성 인덱스
feature_name = x_test.columns[feature_idx]

# dependence_plot 시각화
shap.dependence_plot(feature_idx, shap_values, x_test, feature_names=x_test.columns)
plt.show()

 

각각의 shap 값 가져오기

import shap

# SHAP 값 계산
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(x_test)

# 특성 이름 가져오기
feature_names = x_test.columns.tolist()

# SHAP 값과 특성 이름 매칭
feature_importance = pd.Series(shap_values.mean(0), index=feature_names)

# 출력
print(feature_importance.sort_values(ascending=False))

 

중요도 시각화

# 아래에 필요한 코드를 작성하고 결과를 확인합니다.
fi = feature_im / np.sum(feature_im)
# # Feature 중요도 확인
# # 데이터프레임 만들기
perf_dic = {'feature':fi.index,
            'importance': fi_norm}
df = pd.DataFrame(perf_dic)
df.sort_values(by='importance', ascending=True, inplace=True)

# 시각화
plt.figure(figsize=(6, 6))
plt.barh(df['feature'], df['importance'])
plt.tight_layout()
plt.show()



참조: https://velog.io/@cjkangme/AIVLE-TIL-23.03.10-AI-%EB%AA%A8%EB%8D%B8-%ED%95%B4%EC%84%9D%ED%8F%89%EA%B0%80-2

 

요약해준 선배 기수님에게 감사를 표한다.

반응형