콘텐츠로 이동

ML 해석을 고민한 기록

저자의 말

이 페이지는 SHAP과 모형 해석 가능성에 대한 저자의 실무 경험과 고민을 정리한 글이다. 이론 설명이 아니라, 이론을 실무에 적용하려 할 때 부딪히는 현실적인 벽과, 그 벽을 넘기 위해 어떤 방향으로 공부해왔는지에 대한 기록이다.


7.1 SHAP을 실무에 쓰려 했을 때

2018년, 저자가 데이터 분석 업무를 시작했을 때 SHAP은 이미 Kaggle과 GitHub에서 상당한 주목을 받고 있었다. Lundberg & Lee (2017)의 논문을 읽었고, 실무에서 활용할 방법을 진지하게 고민했다.

SHAP의 구조는 명쾌하다. 차주 \(n\)명, 변수 \(p\)개로 모형을 만들면, SHAP은 \(n \times p\) 크기의 행렬을 반환한다. 각 행은 한 명의 차주이고, 각 열은 해당 변수가 그 차주의 예측에 기여한 몫이다. 이 값들은 log-odds 공간에서 additive하게 작동한다:

\[ \hat{f}(x^{(i)}) = \underbrace{E[\hat{f}(X)]}_{\text{base value (전체 평균 log-odds)}} + \sum_{j=1}^{p} \phi_j^{(i)} \]

한 차주의 SHAP value를 모두 더하면, 전체 평균 대비 그 차주의 예측이 얼마나 높거나 낮은지가 정확히 나온다. 수학적으로는 완벽하다.


7.2 핵심 문제: Local에서만 유효하다

그런데 실무에서 쓰려고 하면 근본적인 문제에 부딪힌다. 같은 변수의 같은 값이라도, 차주마다 SHAP value가 다르다.

로지스틱 회귀에서는 이런 일이 없다. 변수 \(x_1\)의 값이 1이면, A 차주든 B 차주든 모형이 부여하는 가중치는 \(\beta_1\)으로 동일하다. 해석이 globally 일관적이다.

트리 기반 모형에서는 다르다. \(x_1\)의 값이 같더라도, \(x_2\)가 다르면 트리의 분기 경로가 달라진다. \(x_1 \times x_2\), \(x_1 \times x_3\) 등의 교호작용(interaction) 때문에, \(x_1\)에 대한 SHAP value가 차주마다 달라지는 것이다. SHAP은 이 교호작용을 각 변수에 "공정하게 분배"하는데, 그 분배 결과가 차주의 다른 변수 값에 따라 달라진다.

이것이 SHAP이 locally만 해석 가능하다는 의미다. "DTI가 높으면 모형이 위험하다고 판단한다"라고 일반화할 수 없다. "이 차주의 DTI가 높고, 소득이 낮고, 연체 이력이 있을 때, DTI의 SHAP value는 +0.3이다"라고 개별적으로만 말할 수 있다.


7.3 결국 실무에서 쓴 것: |SHAP|의 평균

결론적으로, 저자가 SHAP을 주로 활용한 방식은 변수 중요도 용도였다.

\[ \text{Feature Importance}_j = \frac{1}{n} \sum_{i=1}^{n} |\phi_j^{(i)}| \]

각 변수의 SHAP value에 절댓값을 취하고, 전체 차주에 대해 평균을 낸다. 양의 기여든 음의 기여든, 모형 안에서 그 변수가 얼마나 큰 영향력을 행사하는지를 본 것이다. 이렇게 하면 globally 해석이 된다.

개별 차주의 waterfall plot이나 local 해석은 실무에서 적극적으로 활용하기 어려웠다. 이유는 두 가지다:

  1. 현업 설명의 어려움 — SHAP의 이론적 배경 자체가 무겁다. 게임이론, Shapley Value, 조합론적 가중 평균, 그리고 TreeSHAP의 다항 시간 근사까지. 이걸 심사 담당자나 경영진에게 설명해서 "왜 이 고객이 거절되었는가"의 근거로 쓰기에는 진입 장벽이 너무 높았다.

  2. 일관성의 부재 — 같은 변수, 같은 값인데 차주마다 기여도가 다르다는 것은, 규칙 기반의 심사 체계에서는 받아들이기 어렵다. "DTI 60% 이상이면 감점 몇 점"이라는 일관된 규칙을 기대하는 현업에게, "이 고객의 DTI 기여도는 다른 변수와의 조합에 따라 달라집니다"라는 답변은 실질적으로 쓸 수 없었다.


7.4 구현하지 못한 시나리오 — "어떻게 하면 승인되나요?"

SHAP을 global 변수 중요도 용도로만 쓰면서도, 머릿속에서 계속 맴돌던 시나리오가 하나 있었다. 대출 심사에서 거절된 차주가 항의했을 때, SHAP의 local 해석으로 무엇을 할 수 있을까?

차주 홍길동이 변수 \(x_1, \ldots, x_p\)의 관측치를 가지고 있고, 모형이 산출한 log-odds가 전체 평균 + \(\phi_1 + \cdots + \phi_p\)로 분해된다. 여기까지는 좋다. 그런데 이 차주가 거절되었고, "어떻게 하면 승인이 되느냐"고 물어오면?

가장 자연스러운 접근은, SHAP value가 크게 작용한 변수를 중심으로 값을 조금씩 바꿔보면서 승인 경계를 넘는 조합을 찾는 것이다. 그러나 이것을 실제로 구현하려고 하면 두 가지 벽에 부딪혔다.

첫째, 변수 간 의존성. 예를 들어 \(X_1\)이 "최근 6개월 내 은행업권 대출 건수"이고 \(X_2\)가 "최근 6개월 내 전체업권 대출 건수"라면, \(X_1\)을 1건에서 2건으로 바꿔보는 순간 \(X_2\)도 당연히 영향을 받는다. 현실에서 변수 하나만 독립적으로 움직이는 것은 불가능한 경우가 많다. 이 의존 관계를 모든 변수 조합에 대해 설계하는 것은 쉬운 일이 아니었다.

둘째, 설명 의무의 범위. 변수 의존성을 무시하고 단일 변수만 조정해서 승인 구간에 도달하는 경로를 찾았다고 치자. 그렇다면 이것을 고객에게 알려줘야 하는가? "고객님, 대출을 1건 줄이시면 승인될 수 있습니다"라고 안내할 의무가 있는 것인가? 솔직히 말하면, 저자는 은행 심사역이나 여신 실무에서 일한 적이 없다. CB사에서 모형을 만드는 쪽이었지, 그 모형이 현장에서 어떤 프로세스를 거쳐 고객과 만나는지 — 거절 시 어디까지 설명해야 하는지, 어떤 법적 의무가 있는지 — 를 정확히 알지 못했다. 그래서 이 시나리오는 "기술적으로 구현할 수 있을까?"라는 호기심 단계에서 멈췄고, "현장에서 실제로 필요한가?"라는 질문에는 답하지 못한 채 남아 있었다.

결국 구현하지는 못했다. 그런데 이 가이드북을 정리하면서 알게 된 것은, 이 고민이 XAI 문헌에서 Counterfactual Explanation(반사실적 설명) 또는 Algorithmic Recourse(알고리즘적 구제)라는 이름으로 이미 활발히 연구되고 있다는 사실이었다. 역시나 현업에서 나와 유사한 고민을 한 선배들이 많았던 것이다.

거절 사유 고지 의무

한국 신용정보법과 미국 ECOA(Equal Credit Opportunity Act)는 대출 거절 시 거절 사유의 고지를 의무화하고 있다. 그러나 "어떻게 하면 승인된다"는 구체적 개선 방안의 제시까지 요구하지는 않는다. 오히려 지나치게 구체적인 안내는 모형의 역공학(gaming)을 유발할 위험이 있다.


7.5 해석에 대한 고민들

SHAP의 local 한계를 느끼면서도, 당장의 업무에 치여 이론 공부에 손을 놓고 있었다. 돌이켜 보면, 그 사이사이에 해석 가능성에 대한 고민은 계속 이어지고 있었다.

Surrogate Model을 접하다

블랙박스 ML 모형의 예측값을 종속변수로 놓고, 소수의 해석 가능한 변수로 이를 근사하는 회귀 모형을 따로 만드는 접근법이었다. 여기서 중요한 것은, 타겟이 부도 여부(0/1)가 아니라 모형이 산출한 평점(연속형 변수)이라는 점이다. 즉 로지스틱 회귀가 아니라 일반 회귀(regression) 문제로, 원래 모형의 출력을 소수의 변수로 "흉내 내는" 것이다.

개념은 이해했지만, 실제로 적용해보면 한계가 분명했다. Surrogate 모형의 \(R^2\)가 상당히 낮게 나왔다. 원래 모형이 100개 변수의 복잡한 XGBoost라면, 10개 변수의 회귀 모형으로는 그 출력을 충분히 설명하지 못하는 것이다. Surrogate는 결국 원래 모형의 근사(approximation)일 뿐이고, 근사의 충실도(fidelity)가 낮으면 "원래 모형은 이렇게 작동한다"가 아니라 "대략 이렇게 작동하는 것 같다"를 보여주는 셈이었다. 실무에서 이 방식을 채택하지는 않았다.


1-Depth GBM을 우연히 알게 되다

구글링을 하다가 우연히, GBM에서 stump(depth=1)로만 구성된 트리 앙상블은 해석 가능한 형태로 변환할 수 있다는 것을 알게 되었다. 각 트리가 변수 하나만 사용하므로, 최종 모형이 각 변수의 독립적인 비선형 함수의 합 — 즉 GAM(Generalized Additive Model) — 이 된다는 원리였다.

이것을 구현한 파이썬 패키지도 확인했고, 이론적으로 점검했다. 그 과정에서 KCB(코리아크레딧뷰로)가 이 방식에 대한 특허를 보유하고 있다는 사실도 알게 되었다. depth=1이라는 제약을 두면 당연히 deep tree 기반 모형보다 성능은 낮아질 수밖에 없다. 하지만 누구나 생각할 수 있을 법한 아이디어를, 실제 실무에 적용하고 특허까지 확보했다는 점이 대단하다고 느꼈다. Surrogate처럼 근사하는 것이 아니라, 모형 구조 자체가 해석 가능한 형태라는 것 — 이건 실무에서 쓸 수 있겠다고 생각했다.


EBM을 알게 되다

1-Depth GBM을 공부하던 중, EBM(Explainable Boosting Machine)의 존재를 알게 되었다. Microsoft Research가 만든 InterpretML 라이브러리의 핵심 모형이었다. 고려대학교 DMQA 연구실의 세미나 자료를 통해 대략적인 이론을 접했고, "학습 단계에서부터 변수 효과를 분리하는" 접근이 있다는 것을 이해했다. 그러나 솔직히 말하면, 바쁘다는 핑계로 깊이 파고들지는 못했다.


이 가이드북을 정리하면서

이 가이드북을 정리하면서 Functional ANOVA 분해의 세계를 본격적으로 공부하게 되었다.

\[ f(\mathbf{x}) = f_0 + \sum_i f_i(x_i) + \sum_{i<j} f_{ij}(x_i, x_j) + \cdots \]

SHAP이 교호작용을 각 변수에 분배(distribute)하는 방식이라면, Functional ANOVA는 교호작용을 분리(separate)하는 방식이다. main effect는 main effect대로, interaction은 interaction대로 따로 식별한다. 그리고 이 사상이 EBM, 1-Depth GBM, Purification (Lengerich et al., 2020)까지 하나의 줄기로 이어진다는 것을 알게 되었다.

Hooker (2004) ─── Functional ANOVA for ML
    │
    ├── Lou & Caruana (2012-2013) ─── GA²M → EBM (ante-hoc)
    │     "학습할 때부터 분리하자"
    │
    └── Lengerich et al. (2020) ─── Purification (post-hoc)
          "학습 후에 분리하자"

2018년에 SHAP을 처음 접했을 때 느꼈던 "local에서만 유효하다"는 한계, 그리고 "globally 일관된 해석을 하고 싶다"는 욕구가 결국 Functional ANOVA라는 프레임워크로 귀결된다는 것을 깨달았다. 세부적인 이론은 아직 더 공부해야 하지만, 흩어져 있던 퍼즐 조각들이 맞춰지는 느낌이다.


7.6 왜 SHAP은 교과서가 되고, fANOVA는 아닌가

이 가이드북을 쓰면서 계속 머릿속에 맴돈 질문이 있다. SHAP은 ML 해석의 사실상 표준이 되어 수많은 실무자가 사용하고 있는데, Functional ANOVA는 왜 그만큼 성행하지 않았을까?

fANOVA가 SHAP보다 못한 프레임워크여서가 아니다. 오히려 global 해석이라는 점에서는 fANOVA가 더 근본적인 답을 제공한다. 그런데도 실무에서 압도적으로 SHAP이 쓰이는 이유를 돌이켜보면, 결국 워크플로우의 차이에 있다고 생각한다.

SHAP이 지배적인 이유:

SHAP은 기존 워크플로우를 건드리지 않는다. XGBoost든 LightGBM이든 원하는 대로 학습하고, 끝나고 나서 shap.Explainer(model) 한 줄이면 된다. 모형을 바꿀 필요가 없다. Lundberg가 만든 shap 라이브러리는 waterfall, beeswarm, dependence plot까지 시각화가 완비되어 있고, 결과도 단순하다 — 변수당 숫자 하나. 보고서 한 장에 넣기 좋다.

fANOVA가 성행하지 못한 이유:

fANOVA는 워크플로우를 바꿔야 한다. ante-hoc 방식(EBM, 1-Depth GBM)은 모형 자체를 바꿔야 하고, post-hoc 방식(Purification)도 GAM 구조에서만 깔끔하게 작동한다. "지금 쓰는 XGBoost depth-6 그대로 두고 fANOVA 해석만 붙여주세요"가 안 된다. 결과물도 숫자 하나가 아니라 곡선과 heatmap — 더 풍부한 정보이지만, 정리하기는 더 어렵다. 수학적 진입 장벽(직교 분해, 분포 의존성, non-identifiability)도 SHAP보다 높다.

정리하면 이렇다:

SHAP fANOVA
워크플로우 모형은 그대로, 해석만 추가 해석을 위해 모형부터 다시 설계
결과물 변수당 숫자 하나 변수당 곡선 하나 + 교호작용 heatmap
해석 수준 local (global은 |SHAP| 평균으로 근사) 태생적으로 global
진입 장벽 라이브러리 한 줄 모형 구조 변경 + 수학적 이해 필요

대부분의 실무자는 전자를 택할 수밖에 없다. 모형을 바꾸는 것은 성능 리스크를 수반하고, 조직 내 합의도 필요하다. SHAP은 그 부담 없이 "설명을 붙일 수 있다"는 점에서 압도적으로 편리하다.

그러나 이 가이드북에서 계속 이야기해온 것처럼, SHAP의 global 해석은 local 값을 모아서 흉내 낸 것이고, fANOVA는 태생적으로 global이다. "DTI가 모형에 미치는 효과"를 곡선 한 줄로 보여줄 수 있다는 것 — 이것이 fANOVA 계열(1-Depth GBM, EBM)의 진짜 강점이고, SHAP으로는 대체할 수 없는 가치다.

이론을 알고 도구를 쓰자

SHAP은 강력한 도구다. 그러나 이론적 배경 — Shapley Value의 조합론적 정의, TreeSHAP의 다항 시간 알고리즘, 그리고 local 해석의 본질적 한계 — 을 이해하지 않고 라이브러리만 돌리면, summary plot의 막대 그래프를 보면서 "변수 중요도가 높다/낮다"를 말하는 수준에 머물게 된다. 이론을 어느 정도 이해한 뒤에 도구를 활용하는 것이, 결국 더 정확하고 신뢰할 수 있는 분석으로 이어진다.

부록: 수학적 기초

이 페이지에서 다 풀지 못한 이야기 — Non-identifiability 문제, fANOVA 직교 분해의 수학적 제약 조건, Purification 알고리즘, SHAP과 fANOVA의 본질적 차이 — 는 부록 A: SHAP과 Functional ANOVA에서 자세히 정리했다.