AI 기초

GUIDE / 7번째 글

편향과 설명: 모델의 결정을 열어 보고 고르게 만들기

데이터·모델·배포 단계에서 편향이 스며드는 경로와 네 가지 공정성 정의, 불가능성 정리, 전처리·인처리·후처리 완화 기법을 정리하고, 편향의 원인을 찾고 설명 요구권에 답하기 위한 SHAP·LIME·Grad-CAM·어텐션 맵을 한 편에서 다룬다.

PALDYN Team46 MIN READ

지난 글에서 AI 시스템이 어긋나는 자리들을 갈라 보고, 모델 학습부터 시스템 설계와 규제까지 그것을 사람의 의도에 맞추는 겹겹의 방어를 훑었다. 이 글은 그중 하나를 붙든다. 편향(bias)은 학습 데이터나 모델 설계에 내재된 체계적 오류로, 특정 집단에게 일관되게 불리한 결과를 내는 것이다. 어긋남 가운데 가장 구체적이고, 가장 자주 법정에 서고, 그래서 가장 먼저 숫자로 재야 하는 자리다.

편향과 설명을 한 편에 묶는 이유가 있다. 편향은 결과의 통계로 드러나지만, 그것을 고치려면 모델이 왜 그 결정을 내렸는지를 봐야 한다. 합격률이 집단마다 다르다는 것을 아는 것과, 어느 입력이 그 차이를 만들었는지를 아는 것은 다른 일이다. 앞의 것은 공정성 지표가 하고 뒤의 것은 설명 가능한 AI(Explainable AI, XAI)가 한다. 그래서 이 글의 앞 절반은 편향을 재고 줄이는 법이고, 뒤 절반은 그 작업이 막히는 자리에서 모델을 열어 보는 법이다.

편향의 발생 경로

단계별 편향 유형

편향을 「나쁜 데이터」 하나로 설명하면 부족하다. 편향은 데이터 수집, 모델 학습, 배포의 세 단계에서 각각 따로 생기고, 한 단계를 고쳐도 다른 단계가 다시 만든다.

데이터 단계에는 셋이 있다. 표본 편향은 특정 집단이 데이터에 실제보다 많거나 적게 실린 것이다. 레이블 편향은 정답을 붙인 사람의 판단이 데이터에 실린 것이고, 역사적 편향은 데이터가 정확히 수집됐는데도 그 안에 과거의 불평등이 그대로 들어 있는 것이다. 셋째가 가장 다루기 어렵다 — 데이터에 아무 오류가 없기 때문이다. 세상이 그랬고 데이터는 그것을 옮겨 적었을 뿐이라 데이터 품질 검사로는 잡히지 않는다.

모델 단계에도 셋이 있다. 집계 편향은 전체 정확도 하나로 성능을 재면서 하위 집단의 차이를 못 보는 것이다. 정확도 95%인 모델이 어느 집단에서는 80%일 수 있는데 평균은 그것을 숨긴다. 목적함수 편향은 손실함수가 다수 집단의 오차를 줄이는 쪽으로 기울어, 소수 집단은 틀려도 손실이 별로 안 오르는 것이다. 과적합 편향은 그 결과로 다수 집단의 패턴만 세밀하게 배우는 것이다.

배포 단계의 셋은 모델 밖에서 생긴다. 확증 편향 루프는 편향된 결과가 다음 학습 데이터가 되어 편향을 키우는 것이고, 자동화 편향은 사람이 AI의 결과를 검토 없이 믿는 것이다. 맥락 편향은 학습 맥락과 배포 맥락이 다른 것이다 — 한 병원의 데이터로 배운 모델을 인구 구성이 다른 병원에 두는 식이다. 실제 편향은 이 아홉이 겹쳐서 나오므로 한 단계만 고쳐서는 끝나지 않는다.

AI 편향의 유형과 발생 경로

채용 AI의 되먹임 고리

세 단계가 한 시스템에서 어떻게 겹치는지 채용 AI로 따라가 보자. 과거 채용 데이터를 불러오니 남성이 80%, 여성이 20%다. 표본 편향이고 동시에 역사적 편향이다. 그 데이터의 정답은 과거의 채용 결정인데, 그 결정 자체가 같은 스펙의 여성 지원자를 덜 뽑았다면 레이블 편향까지 얹힌다.

모델은 성별 열을 아예 지워도 성별을 배운다. 지원서에 「골프 동아리」가 있으면 합격 확률이 오른다는 규칙은 데이터에 실제로 있고, 그 항목이 남성 지원자에게 훨씬 자주 나온다면 모델은 골프를 통해 성별을 읽는 셈이다. 이렇게 보호 속성과 상관관계가 있어 그 속성을 대신 실어 나르는 특성을 프록시 변수(proxy variable)라 한다. 우편번호가 인종을, 이름이 성별을, 고용 형태가 나이를 나르는 식이고, 상관이 있는 특성을 전부 지우면 모델에 남는 특성이 없다.

배포하면 고리가 닫힌다. 편향된 모델이 남성을 더 많이 뽑고, 그 결과가 다음 해 학습 데이터가 되고, 다음 모델은 조금 더 편향된다. 실제 세계의 불평등이 데이터에 실리고, 그 데이터로 배운 모델이 불평등을 자동화하고 규모를 키운다. 사람 심사관 한 명의 편견은 그가 본 지원서에만 미치지만 모델의 편견은 모든 지원서에 같은 방식으로 미친다. 아마존이 이력서 평가 도구를 시험하다 「여성」이 든 표현을 감점하는 것을 발견하고 폐기했다는 보도가 있었는데, 정확히 이 고리의 첫 바퀴에서 잡힌 사례다.

얼굴 인식과 언어 모델

이미지와 텍스트에서도 같은 경로가 보인다. Buolamwini와 Gebru의 2018년 연구는 상용 얼굴 분석 서비스 셋의 성별 분류 오류율을 피부색과 성별로 갈라 쟀다. 밝은 피부의 남성에서는 오류율이 1%를 밑돌았지만 어두운 피부의 여성에서는 최대 34.7%였다. 학습 데이터의 인종·성별 구성이 그대로 성능 격차가 된 표본 편향이고, 전체 정확도만 봤다면 안 보였을 집계 편향이기도 하다.

언어 모델은 역사적 편향의 교과서다. 「의사」 옆에 「그」가, 「간호사」 옆에 「그녀」가 오는 빈도가 학습 텍스트에 그대로 있으니 모델은 그것을 배우고, 번역이나 문장 완성에서 그 연상을 되돌려준다. 오류가 아니라 통계의 충실한 재현이라는 점이 다루기 어려운 이유다 — 데이터를 「고치면」 세상에 없는 분포를 만드는 것이고, 안 고치면 고정관념을 규모 있게 재생산하는 것이다.

공정성 지표

집단별 혼동 행렬

편향을 재려면 먼저 보호 속성(sensitive attribute) — 성별·인종·나이처럼 그것으로 차별해서는 안 되는 속성 — 을 정하고, 그 값으로 데이터를 갈라 집단마다 혼동 행렬을 따로 센다. 혼동 행렬은 정답과 예측의 조합 넷 — 맞힌 긍정(TP), 잘못된 긍정(FP), 놓친 긍정(FN), 맞힌 부정(TN) — 을 센 2×2 표이고, 그 네 칸에서 나오는 세 숫자가 이 절의 재료다.

from sklearn.metrics import confusion_matrix

def group_rates(y_true, y_pred, sensitive):
    rates = {}
    for g in sensitive.unique():
        m = sensitive == g
        tn, fp, fn, tp = confusion_matrix(y_true[m], y_pred[m]).ravel()
        rates[g] = {
            "acceptance": (tp + fp) / m.sum(),   # 합격률
            "tpr": tp / (tp + fn),               # 실력자 합격률
            "fpr": fp / (fp + tn),               # 오합격률
        }
    return rates

합격률은 그 집단에서 모델이 긍정으로 판정한 비율이다. 정답과 무관하게 「몇 명을 통과시켰는가」만 본다. TPR(true positive rate)은 정답이 긍정인 사람 중 모델이 긍정으로 맞힌 비율이고, 채용이면 실력 있는 지원자의 합격률이다. FPR(false positive rate)은 정답이 부정인 사람 중 잘못 긍정으로 판정한 비율이다. 세 숫자를 집단마다 나란히 놓으면 공정성 정의 대부분이 「이 중 어느 것을 집단 간에 같게 할 것인가」로 읽힌다.

공정성 정의

네 정의를 한 시나리오에 대 보자. 채용 AI가 남성 지원자의 70%, 여성 지원자의 40%를 합격시켰다. 이 숫자 하나로 불공정하다고 말할 수 있는지가 정의마다 갈리고, 아래 그림이 그 넷을 한 장에 세운 것이다.

공정성 지표와 미탐지 예시

인구통계 동등성(demographic parity)은 합격률이 집단 간에 같아야 한다는 정의다. 70% 대 40%는 곧바로 위반이다. 결과의 평등을 직접 보장하지만, 집단 간에 실제 자격 분포가 다를 때 그 차이를 무시하고 맞추게 된다.

균등 기회(equalized odds)는 TPR과 FPR이 집단 간에 같아야 한다는 정의다. 실력 있는 사람의 합격률이 같고, 실력 없는 사람의 오합격률도 같아야 한다. 합격률 자체는 달라도 된다 — 자격 있는 사람의 비율이 집단마다 다르면 합격률도 다른 것이 오히려 자연스럽다. 능력 기준의 공정성이지만 「정답」이 곧 과거의 결정이라면 레이블 편향을 그대로 기준으로 삼는 셈이다.

보정 공정성(calibration)은 예측 점수의 뜻이 집단 무관하게 같아야 한다는 정의다. 70점을 받은 사람은 어느 집단이든 실제로 70%쯤 성공해야 한다. 점수를 믿고 쓰는 쪽 — 판사, 심사관 — 에게 필요한 성질이고, 결과의 불평등은 허용한다.

개인 공정성(individual fairness)은 집단 통계에서 벗어나 「비슷한 개인은 비슷한 결과를 받아야 한다」고 요구한다. 고정관념에서 자유롭지만 「비슷하다」를 무엇으로 잴지 정해야 하고, 그 유사도 정의에 편향이 다시 들어올 수 있다.

불가능성 정리와 COMPAS

넷을 다 만족하면 되지 않느냐는 질문에 답이 있고, 그 답을 준 정리가 둘이다. Chouldechova(2017)는 양성 예측값(PPV) — 긍정으로 판정된 사람 중 정답도 긍정인 비율, 재범 예측이면 고위험 판정을 받은 사람 중 실제로 재범한 비율 — 과 FPR·FNR 사이의 관계식을 세웠다. 기저율(base rate)을 정답이 긍정인 사람의 실제 비율 pp 라 하면 어느 분류기든 다음이 성립한다.

FPR=p1−p⋅1−PPVPPV⋅(1−FNR)\text{FPR} = \frac{p}{1-p}\cdot\frac{1-\text{PPV}}{\text{PPV}}\cdot(1-\text{FNR})

두 집단의 기저율이 다른데 PPV와 FNR을 같게 맞추면 FPR은 다를 수밖에 없다 — 항등식이라 어느 알고리즘을 써도 피할 수 없다. Kleinberg·Mullainathan·Raghavan(2016)은 점수 자체를 두고 같은 종류의 결론을 냈다. 보정, 그리고 실제 긍정인 사람들의 평균 점수와 실제 부정인 사람들의 평균 점수가 각각 집단 간에 같다는 두 균형 조건을 셋 다 만족하는 것은 예측이 완벽하거나 기저율이 같을 때뿐이라는 증명이다. 인구통계 동등성과 보정도 대개 같이 못 간다 — 점수가 보정되어 있고 기저율이 다르면 같은 임계값에서 합격률은 다를 수밖에 없다. 그러니 무엇을 공정이라 부를지 먼저 정해야 지표를 고를 수 있다. 순수하게 기술적인 문제가 아니라는 말은 이 뜻이다.

미국 법원이 쓰던 재범 위험 예측 도구 COMPAS가 이 충돌의 교과서다. 2016년 ProPublica의 분석에서 재범하지 않았는데도 고위험으로 분류된 비율이 흑인 피고 쪽이 백인 피고의 두 배 가까이 나왔다. FPR이 다르니 균등 기회 위반이다. 제작사는 같은 점수를 받은 사람의 재범률이 인종과 무관하게 같다고 반박했다 — 보정은 만족한다는 뜻이다. 양쪽 다 숫자로는 맞았고, 기저율이 다른 두 집단에서 둘을 동시에 만족할 수는 없었다. 어느 정의를 택할지는 통계가 아니라 「피고에게 부당한 구금이 더 나쁜가, 판사에게 믿을 수 없는 점수가 더 나쁜가」라는 판단이 정한다.

COMPAS를 둘러싼 두 주장과 불가능성 항등식

편향 감사 도구

이 계산을 직접 짤 필요는 없다. 마이크로소프트에서 시작해 2021년부터 중립 거버넌스로 넘어간 Fairlearn은 보호 속성으로 갈라 지표를 세는 MetricFrame과 정의별 차이 함수를 준다.

from fairlearn.metrics import MetricFrame, demographic_parity_difference
from sklearn.metrics import accuracy_score

mf = MetricFrame(metrics=accuracy_score, y_true=y_test, y_pred=y_pred,
                 sensitive_features=X_test["gender"])
print(mf.by_group)        # 집단별 정확도
print(mf.difference())    # 집단 간 최대 차이
print(demographic_parity_difference(y_test, y_pred,
                                    sensitive_features=X_test["gender"]))

by_group이 앞 절의 집계 편향을 잡는 자리다. 전체 정확도 한 줄 아래에 집단별 정확도가 나란히 서고, difference()가 가장 벌어진 두 집단의 간격을 준다. 시카고대학에서 시작한 Aequitas는 의사결정 시스템의 편향 감사에 맞춰진 도구로, 어느 정의를 볼지 고르는 결정 트리부터 안내한다. IBM이 만들어 2020년 리눅스 재단으로 넘긴 AI Fairness 360은 70개 안팎의 지표와 열 개 남짓의 완화 알고리즘을 한 패키지에 묶었다. 셋 다 지표를 세는 것은 하지만 무엇을 보호 속성으로 두고 어느 정의를 택할지는 정해 주지 않는다 — 그 결정이 앞 소절의 몫이다.

편향 완화 기법

전처리

완화 기법은 학습 파이프라인의 어디에 손을 대느냐로 셋으로 나뉜다. 전처리는 학습 데이터에 손을 댄다. 소수 집단을 더 뽑거나(오버샘플링) 다수 집단을 덜 뽑거나(언더샘플링), 표본마다 가중치를 줘서 집단과 정답의 조합이 독립이 되도록 맞춘다. 남성 합격 표본이 너무 많으면 그 표본의 가중치를 내리고 여성 합격 표본의 가중치를 올리는 식이다.

가장 단순하고 어느 모델에나 붙지만, 원본 분포를 바꾼다는 부작용이 있다. 세상에 없는 분포로 배운 모델은 보정이 깨진다 — 여성 합격 표본을 부풀리면 여성에게 주는 점수의 뜻이 남성과 달라진다. 그리고 데이터를 바꾼 것이지 모델을 바꾼 것이 아니라, 프록시 변수를 통해 새는 편향은 그대로 남을 수 있다.

인처리

인처리(in-processing)는 학습 목적함수에 공정성 제약을 얹는다. 정확도를 최대화하되 집단 간 합격률 차이가 어느 한도 안이어야 한다는 조건을 함께 푸는 것이다.

from fairlearn.reductions import GridSearch, DemographicParity
from sklearn.linear_model import LogisticRegression

mitigator = GridSearch(LogisticRegression(), DemographicParity(), grid_size=10)
mitigator.fit(X_train, y_train, sensitive_features=X_train["gender"])
best_model = mitigator.predictors_[mitigator.best_idx_]   # mitigator.predict()가 쓰는 모델

GridSearch는 제약의 세기를 격자로 바꿔 가며 모델을 여럿 학습하고, 후보 전부를 predictors_에, 각 후보의 오차를 objectives_에, 제약 위반의 크기를 gammas_에 같은 순서로 남긴다. best_idx_는 그중 오차와 위반을 constraint_weight로 저울질해 고른 하나를 가리키고 predict()가 그 모델을 쓴다. 하지만 인처리의 진짜 산출물은 그 하나가 아니라 세 목록을 함께 그린 정확도와 공정성의 트레이드오프 곡선이다. 합격률 차이를 0으로 만들면 정확도가 얼마나 내려가는지, 차이를 5%p까지 허용하면 얼마나 회복되는지가 눈에 보이고, 어디에 설지는 사람이 정한다. 대신 학습 코드를 바꿔야 하므로 이미 배포된 모델이나 외부 API 뒤의 모델에는 못 쓴다.

후처리

후처리는 학습이 끝난 모델의 출력에 손을 댄다. 가장 흔한 방식이 집단마다 다른 임계값을 두는 것이다. 남성은 점수 0.6 이상, 여성은 0.5 이상을 합격으로 잡아 TPR을 맞추는 식이다. 모델을 다시 학습하지 않으므로 배포된 모델에도 바로 붙고, 어느 정의를 목표로 할지를 임계값 조정만으로 바꿀 수 있다.

단점은 둘이다. 추론 시점에 집단 정보가 있어야 한다 — 지원자의 성별을 모르면 어느 임계값을 쓸지 모른다. 그리고 집단마다 다른 기준을 적용하는 것 자체가 법역과 도메인에 따라 차별로 취급될 수 있다. 결과를 고르게 만들려고 절차를 다르게 하는 것이니, 이것이 허용되는 자리인지는 법률가가 먼저 봐야 한다.

지표의 한계

세 기법을 다 써도 남는 자리가 있다. 맥락 의존성이 첫째다. 채용에서는 합격률이, 의료 진단에서는 놓친 환자의 비율이, 신용 심사에서는 점수의 보정이 중요하고, 같은 지표 값이 도메인마다 다른 뜻이다. 한 도메인에서 고른 정의를 다른 도메인에 들고 가면 안 된다.

인과 공정성이 둘째다. 지금까지의 지표는 전부 관찰된 결과의 통계이고, 결과가 어떤 경로로 나왔는지는 안 본다. 그래서 프록시 변수를 통한 간접 차별은 지표상 공정해 보일 수 있다 — 보호 속성 열을 지우고 합격률까지 맞췄어도, 결정의 근거가 여전히 프록시라면 그 모델은 지표는 통과했지만 근거는 그대로다. 결과가 아니라 경로를 물어야 하는 자리다.

사회 변화가 셋째다. 지금 데이터로 잰 공정성이 내년에도 공정할 보장이 없다. 집단의 구성이 바뀌고 법이 바뀌고 무엇을 보호 속성으로 볼지도 바뀐다. 한 번 감사하고 끝나는 일이 아니라 배포 뒤에도 주기적으로 재야 하는 값이다.

남는 세 자리 중 둘째가 이 글의 뒤 절반을 부른다 — 결과가 아니라 경로를 물으려면 모델 안을 봐야 한다.

설명 가능한 AI

블랙박스와 프록시 변수

딥러닝 모델은 수억 개의 파라미터로 패턴을 배운다. 성능은 뛰어나지만 어느 입력이 어떤 경로로 출력에 닿았는지를 사람이 따라갈 수 없고, 이 불투명성을 블랙박스 문제라 한다. 로지스틱 회귀는 계수를 읽으면 됐지만 그래디언트 부스팅 트리 수백 그루나 트랜스포머 층 수십 개에는 읽을 계수가 없다.

편향 감사에서 이것이 막히는 지점은 정확히 프록시다. 성별을 지웠는데 합격률이 다르다면 모델이 무엇으로 성별을 읽고 있는지 찾아야 하는데, 특성이 200개면 어느 것이 프록시인지 상관계수만으로는 안 가려진다. 상관은 높지만 모델이 안 쓰는 특성도 있고, 상관은 낮은데 다른 특성과 조합되어 쓰이는 특성도 있다. 필요한 것은 모델이 실제로 그 특성에 얼마나 기댔는가이고, 그것을 재는 도구가 설명 기법이다.

설명 요구권과 규제

설명이 필요한 두 번째 이유는 법이다. 의료 AI가 암을 진단하면 의사는 「왜」를 알아야 치료를 결정할 수 있고, 대출 심사 AI가 거절하면 신청자는 무엇 때문인지 물을 권리가 있다. EU GDPR은 자동화된 결정만으로 개인에게 법적 효력이 있는 결과를 내는 것을 제한하고, 그 결정의 논리에 관한 정보를 받을 권리를 둔다 — 흔히 설명 요구권이라 부르는 조항이다. 지난 글에서 본 EU AI 법도 채용·신용·사법 같은 고위험 영역에 투명성과 사람의 감독을 요구한다. 설명은 좋은 관행이 아니라 규제 요건이 되어 가고 있다.

여기서 설명의 소비자가 셋이라는 것을 갈라 두면 좋다. 규제 기관은 시스템 전체가 어떤 특성에 기대는지를 묻고, 신청자는 자기 한 건이 왜 거절됐는지를 묻고, 개발자는 모델이 프록시를 쓰는지를 묻는다. 셋이 원하는 설명이 다르고, 그래서 기법도 갈린다.

설명 범위와 모델 의존성

설명 기법은 두 축으로 분류한다. 첫째는 설명 범위다. 전역(global) 설명은 모델 전체의 동작을 말한다 — 「어떤 특성이 전반적으로 중요한가」. 지역(local) 설명은 예측 하나를 말한다 — 「이 사람의 대출이 왜 거절됐는가」. 규제 기관과 개발자가 전역을, 신청자가 지역을 원한다.

둘째는 모델 의존성이다. 모델 불문(model-agnostic) 기법은 모델을 입력과 출력만 있는 함수로 다루므로 무엇에든 붙는다. 모델 특화(model-specific) 기법은 트리의 분기 경로나 CNN의 특징 맵처럼 아키텍처 안쪽 구조를 쓰므로 그 구조가 있는 모델에만 붙지만 그만큼 정확하다.

XAI 기법 분류

네 칸에 무엇이 들어가는지 짚어 두면 뒤에서 기법 이름을 만났을 때 자리가 잡힌다. 전역·불문에는 한 특성의 값을 바꿔 가며 예측의 평균 변화를 그리는 부분 의존성 그림(PDP)과, 한 특성을 뒤섞었을 때 성능이 얼마나 떨어지는지로 중요도를 재는 순열 중요도, 그리고 SHAP 값을 전체 데이터에서 모은 요약 그림이 있다. 전역·특화에는 결정 트리의 경로 추적과 트랜스포머의 어텐션 맵이 있다. 지역·불문에는 LIME과 SHAP, 그리고 「무엇을 얼마나 바꾸면 결과가 뒤집히는가」를 찾는 반사실 설명(counterfactual explanation)이 있다. 지역·특화에는 입력의 그래디언트를 그대로 보는 saliency map, 기준 입력에서 실제 입력까지 그래디언트를 적분하는 Integrated Gradients, 출력을 층마다 거꾸로 배분하는 LRP, 그리고 이미지 한 장의 어느 영역이 그 판정을 만들었는지를 CNN의 특징 맵으로 보는 Grad-CAM이 있다. 그림은 Grad-CAM을 전역 칸에 그려 두었지만, 설명하는 대상이 이미지 한 장의 예측 하나이므로 지역·특화가 맞다. SHAP이 전역과 지역 양쪽에 걸리는 것이 눈에 띄는데, 지역 설명을 모으면 전역이 되는 구조라 그렇다.

설명 기법

SHAP

SHAP(SHapley Additive exPlanations)은 협력 게임이론의 샤플리 값을 특성에 적용한다. 샤플리 값은 여럿이 함께 만든 성과를 각자의 기여로 나누는 방법으로, 한 사람을 모든 가능한 순서로 팀에 넣어 보며 그때마다 늘어난 성과를 평균한 값이다. 특성을 사람으로, 예측값을 성과로 두면 「이 특성이 예측을 얼마나 밀었는가」가 나온다.

핵심 성질이 하나다. 모든 특성의 SHAP 값을 더하고 기대값을 더하면 정확히 그 예측값이 된다. 설명이 예측을 남김없이 재구성하고, 어느 특성에 얼마가 배분됐는지가 빠짐없이 적힌다. 그림의 대출 심사가 그 예다.

SHAP 값 시각화 예시

기대값 — 아무 정보 없이 평균 신청자에게 주는 승인 확률 — 이 0.45다. 연소득 5,000만 원이 +0.18, 신용점수 720이 +0.12, 정규직이 +0.08로 승인 쪽으로 밀고, 부채비율 78%가 −0.20, 연체 이력 2회가 −0.16, 대출 기간 36개월이 −0.06으로 거절 쪽으로 민다. 더하면 0.45+0.38−0.42=0.410.45 + 0.38 - 0.42 = 0.41 이고 그것이 최종 예측이다. 신청자에게 줄 답이 여기서 바로 나온다 — 소득과 신용은 충분했지만 부채비율과 연체 이력이 그것을 상쇄했다.

import shap

explainer = shap.TreeExplainer(model)          # XGBoost·LightGBM 같은 트리 모델
shap_values = explainer.shap_values(X_test)

shap.plots.waterfall(shap.Explanation(         # 예측 하나 — 그림의 폭포
    values=shap_values[0], base_values=explainer.expected_value,
    data=X_test.iloc[0], feature_names=feature_names))
shap.summary_plot(shap_values, X_test)         # 전체 — 전역 중요도
shap.dependence_plot("부채비율", shap_values, X_test)   # 특성 하나의 값과 기여

샤플리 값을 정의대로 계산하면 특성의 부분집합 전부를 봐야 하므로 특성 수에 지수적으로 비싸다. TreeExplainer는 트리 구조를 이용해 이것을 정확하게, 다항 시간에 푼다 — 트리 모델에서 SHAP이 기본 선택인 이유다. 트리가 아니면 KernelExplainer가 표본을 뽑아 근사하는데, 느리고 값이 흔들린다. 딥러닝에는 그래디언트 기반의 DeepExplainer와 GradientExplainer가 있다.

LIME

LIME(Local Interpretable Model-agnostic Explanations)은 설명하려는 예측 하나의 주변에서 복잡한 모델을 단순한 선형 모델로 흉내 낸다. 절차는 셋이다. 그 입력을 조금씩 흔든 표본을 수천 개 만들고, 원래 모델에 넣어 예측을 받고, 원래 입력에 가까운 표본에 큰 가중치를 주며 선형 회귀를 맞춘다. 그 회귀의 계수가 설명이다 — 「이 근방에서는 부채비율이 오르면 승인 확률이 이만큼 내린다」.

from lime.lime_tabular import LimeTabularExplainer

explainer = LimeTabularExplainer(
    X_train.values, feature_names=feature_names,
    class_names=["거절", "승인"], mode="classification",
    kernel_width=None)                    # 근사 영역의 폭 — None이면 특성 수에서 정한다
explanation = explainer.explain_instance(
    X_test.iloc[0].values, model.predict_proba,
    num_features=6, num_samples=5000)     # 상위 6개 특성, 흔든 표본 5,000개
print(explanation.as_list())              # [(특성 조건, 계수), ...]

모델이 무엇이든 predict_proba처럼 입력을 받아 확률을 내는 함수만 있으면 되고, 표 데이터·텍스트·이미지 전부에 같은 절차가 붙는다. 대신 두 가지가 흔들린다. 근사 영역의 크기를 정하는 kernel_width가 설명을 바꾼다 — 좁게 잡으면 그 점 바로 옆만 보고 넓게 잡으면 다른 영역까지 섞인다. 기본값은 특성 수의 제곱근에 0.75를 곱한 값인데, 그 값이 이 데이터에 맞다는 보장은 없다. 그리고 num_samples개의 표본을 무작위로 뽑으므로 같은 입력을 두 번 설명하면 결과가 조금 다르다. 신청자에게 「왜 거절됐는가」를 답할 때 두 번 물으면 두 답이 나오는 도구라는 뜻이고, 뒤에서 볼 안정성 문제의 주인공이다.

Grad-CAM

이미지 분류에서는 「어느 특성」이 아니라 「어느 영역」이 결정에 쓰였는지를 물어야 한다. Grad-CAM은 CNN의 마지막 합성곱 층을 보는 모델 특화 기법이다. 절차는 이렇다. 목표 클래스의 점수를 그 층의 특징 맵으로 미분해 그래디언트를 얻고, 특징 맵마다 그래디언트를 공간 방향으로 평균해 「이 맵이 그 클래스에 얼마나 중요한가」라는 가중치 하나씩을 만든다. 가중치로 특징 맵을 가중합하고 ReLU — 음수를 0으로 자르는 함수 — 를 걸어 클래스에 불리한 영역을 지우면, 클래스에 유리한 영역만 남은 거친 맵이 나온다. 그것을 입력 크기로 키우면 히트맵이다.

구현은 목표 층에 forward hook(순전파 때 그 층의 출력을 가로채는 콜백)과 backward hook(역전파 때 그 층으로 흘러온 그래디언트를 가로채는 콜백)을 걸어 활성과 그래디언트를 받아 두는 것이 전부다. PyTorch에서는 register_forward_hook과 register_full_backward_hook이 그 일을 하고, 둘 다 핸들을 돌려주므로 쓰고 나면 그 핸들의 remove()를 반드시 불러야 한다 — 떼지 않으면 다음 추론마다 활성이 쌓인다. 편향 감사에서 이 히트맵이 잡는 것은 맥락 편향이다. 피부 병변 분류기가 병변이 아니라 옆에 놓인 자(ruler)를 보고 있었다거나, 허스키와 늑대를 가르는 분류기가 동물이 아니라 배경의 눈을 보고 있었다는 식의 지름길을 히트맵이 드러낸다. 검증 정확도는 높은데 그 정확도가 엉뚱한 곳에서 오고 있다는 사실은 숫자로는 안 잡히고 그림으로만 잡힌다.

어텐션 맵

트랜스포머는 자기 설명 장치를 이미 들고 있는 것처럼 보인다. 어텐션 가중치가 토큰마다 어느 토큰을 얼마나 참고했는지를 0과 1 사이의 값으로 적어 두기 때문이다. 허깅페이스 모델에 output_attentions=True를 주면 그 값이 그대로 나온다.

from transformers import AutoTokenizer, AutoModelForSequenceClassification

tokenizer = AutoTokenizer.from_pretrained("klue/bert-base")
model = AutoModelForSequenceClassification.from_pretrained(
    "klue/bert-base", output_attentions=True)
outputs = model(**tokenizer("이 제품은 정말 훌륭합니다.", return_tensors="pt"))
attentions = outputs.attentions              # 층마다 하나씩 (배치, 헤드, seq, seq)
avg_attention = attentions[-1].mean(dim=1)   # 마지막 층의 헤드 평균

outputs.attentions는 층 수만큼의 튜플이고 원소마다 (배치, 헤드, seq, seq) 꼴의 텐서다 — klue/bert-base면 12층 12헤드라 문장 하나에 144장의 시퀀스×시퀀스 행렬이 나온다. 마지막 층의 헤드 평균을 문장 위에 색으로 얹으면 「이 제품은 정말 훌륭합니다」에서 「훌륭」이 진하게 칠해진다.

문제는 높은 어텐션이 곧 중요한 토큰이라는 직관이 늘 성립하지 않는다는 점이다. Jain과 Wallace(2019)는 어텐션 가중치를 다르게 바꿔도 예측이 그대로인 경우와, 어텐션과 그래디언트 기반 중요도가 서로 어긋나는 경우를 여럿 보였다. 층이 열두 개, 헤드가 열두 개인데 그중 무엇을 설명으로 볼지도 정해져 있지 않다 — 위 코드가 마지막 층의 평균을 고른 것도 관행이지 근거는 아니다. 반론도 있어서 어텐션이 설명이 될 조건을 좁혀 잡는 후속 연구가 이어졌지만, 요점은 하나다 — 어텐션 맵은 모델이 어디를 봤는가를 보여 주지 왜 그렇게 답했는가를 보여 주지는 않는다. 설명으로 쓰려면 SHAP이나 Integrated Gradients 같은 다른 기법과 맞대어 확인해야 한다.

설명의 한계와 쓰임

충실성과 안정성

설명을 받았다고 끝이 아니다. 설명 자체를 두 성질로 재야 한다. 충실성(faithfulness)은 설명이 모델의 실제 동작을 얼마나 반영하는가다. LIME은 선형 근사이므로 원래 모델이 그 근방에서 선형이 아니면 설명이 틀린다. 어텐션은 앞 소절에서 본 대로 충실성 자체가 논쟁이다. SHAP은 가산 성질 덕에 예측을 정확히 재구성하지만, 특성 사이의 상관이 강하면 「그 특성이 없을 때」를 만드는 방식에 따라 값이 달라진다. 단순화하는 과정에서 왜곡이 들어오는 것은 기법을 가리지 않는다.

안정성(stability)은 비슷한 입력에 비슷한 설명을 주는가다. 신청자 둘의 소득이 100만 원 차이인데 한쪽 설명에는 소득이 첫째 이유로, 다른 쪽에는 넷째 이유로 서면 어느 쪽 설명도 믿기 어렵다. LIME의 무작위 표본이 여기서 문제가 되고, 실무에서는 num_samples를 늘리거나 random_state로 시드를 고정하거나 여러 번 돌려 평균해 잡는다. 규제 대응으로 설명을 내보낼 때는 같은 입력에 같은 설명이 나온다는 것을 먼저 확인해야 한다.

설명의 악용

설명이 열리면 모델을 우회하는 길도 열린다. 신용 심사의 설명에서 「부채비율이 60% 아래면 승인」을 읽어 낸 뒤 실제 부채를 줄이는 것이 아니라 신고 방식만 바꾸는 식이다. 반사실 설명이 특히 그렇다 — 「무엇을 얼마나 바꾸면 결과가 뒤집히는가」는 신청자가 개선할 방향이기도 하고 조작할 방향이기도 하다.

그래서 설명의 공개 범위를 정하는 것이 설계의 일부다. 신청자에게는 거절의 주된 이유를, 규제 기관에는 전역 중요도를, 개발자에게만 특성 단위의 전체 SHAP 값을 주는 식으로 갈라야 하고, 바꾸기 쉬운 특성 — 신고 항목 — 에 모델이 크게 기대고 있다면 그 자체가 고쳐야 할 신호다. 바꾸기 어려운 특성에 기대는 모델일수록 설명을 열어도 안전하다.

프록시 탐색

앞 절반의 막힌 자리로 돌아오면 절차가 선다. 먼저 공정성 지표로 어느 집단에서 결과가 다른지를 잰다. 다음으로 집단마다 SHAP 값의 평균을 내어 어느 특성이 그 차이를 만드는지를 본다 — 여성 지원자의 점수를 끌어내리는 특성이 골프 동아리라면 그것이 프록시다. 그 특성을 지우거나 가중을 낮춘 뒤 다시 지표를 재고, 새 프록시가 그 자리를 차지하지 않았는지 다시 본다. 프록시는 한 번에 하나만 잡히지 않고, 하나를 지우면 상관이 다음으로 높은 특성이 그 역할을 이어받는 일이 흔하다.

프록시를 찾는 네 걸음

이 절차가 도구 이름을 뺀 이 글의 뼈대다. 지표가 「어디가 다른가」를, 설명이 「무엇이 다르게 만드는가」를, 완화 기법이 「그것을 어떻게 줄이는가」를 맡고, 세 걸음을 배포 뒤에도 되풀이한다. 어느 하나만으로는 안 된다 — 지표만 있으면 이유를 모르고, 설명만 있으면 무엇이 문제인지 기준이 없다.

사람의 몫

기법을 다 갖춰도 세 결정은 사람 몫으로 남는다. 무엇을 보호 속성으로 볼지, 어느 공정성 정의를 택할지, 설명을 누구에게 어디까지 열지. 셋 다 도메인과 법과 영향받는 집단이 정하는 값이라 기술팀 혼자 정하면 틀린다. 편향 문제가 공학 문제인 동시에 사회적·윤리적 문제라는 말은 이 세 결정을 가리키고, 도메인 전문가·법률가·영향받는 집단과의 협업이 장식이 아니라 절차인 이유도 여기 있다. 설명은 도구이고, 그 설명을 읽고 행동으로 옮기는 것은 여전히 사람이다.

이 글은 결과를 숫자로 재고 그 숫자가 막히는 자리에서 모델을 열어 보는 데까지 왔다. 다음 글은 그 반대편을 본다 — 모델이 학습 데이터를 너무 잘 기억하면 열어 볼 필요도 없이 개인정보가 밖으로 새어 나온다. 어떤 공격이 학습 데이터에 누가 있었는지, 무엇이 적혀 있었는지를 뽑아내는지, 그리고 차분 프라이버시·연합학습·동형암호가 각각 어느 지점을 막는지를 다룬다.


읽어주셔서 감사합니다. 😊

LATEST

AI 기초의 최신 글

AI 기초2026.08.19

가드레일을 테스트한다는 것

가드레일 테스트는 단위 테스트와 성질이 다릅니다. 세트를 두 벌로 나누는 이유, 통과 기준을 절대값 대신 기준선으로 잡는 법, 세트가 오염되는 경로, 표본 크기가 결정에 미치는 영향을 정리합니다.

43 MIN
AI 기초2026.08.19

거절도 설계한다: 안 된다고 말하는 법

거절은 안전 장치의 마지막 동작이면서 사용자가 제품을 평가하는 순간입니다. 켜고 끄는 스위치 대신 다섯 칸 사다리를 쓰는 법, 좋은 거절 문구의 조건, 과잉 거절을 재는 방법을 정리합니다.

41 MIN
AI 기초2026.08.19

콘텐츠 조정: 정책을 판정 가능한 것으로 만들기

정책 문서가 있어도 판정은 안 됩니다. 두 사람이 같은 라벨을 붙일 수 있게 정책을 쪼개는 법, 임계값을 둘로 두는 이유, 검토 큐가 터지지 않게 설계하는 법, 라벨마다 따로 봐야 하는 지표를 정리합니다.

50 MIN