AI 윤리·편향 감지·공정성: 프로덕션 AI 시스템의 책임 있는 설계

AI 기술

AI 윤리편향 감지공정성AI 안전성책임 있는 AI

이 글은 누구를 위한 것인가

  • AI 시스템이 특정 그룹에 불공정하게 작동하는지 확인하고 싶은 팀
  • AI 윤리 감사를 준비하거나 투명성 보고서를 작성해야 하는 팀
  • AI 공정성 개념을 코드로 구현하고 싶은 ML 엔지니어

들어가며

AI 채용 시스템이 특정 성별을 선호하고, 대출 AI가 특정 지역을 불리하게 취급한다면 법적·윤리적 문제가 된다. 편향 감지는 모델 개발 단계에서 시작해야 한다.

이 글은 bluefoxdev.kr의 책임 있는 AI 개발 를 참고하여 작성했습니다.


1. AI 편향 유형과 측정 지표

[AI 편향 유형]

데이터 편향:
  훈련 데이터에서 특정 그룹 과소/과다 표현
  역사적 편향 (과거 차별 패턴 학습)
  
알고리즘 편향:
  같은 입력에 다른 그룹이면 다른 출력
  
측정 편향:
  특정 그룹의 데이터 품질이 낮음

[공정성 지표]

인구 동일성 (Demographic Parity):
  P(긍정 결과 | A그룹) = P(긍정 결과 | B그룹)
  예: 남성/여성 채용 확률이 같아야 함

균등 기회 (Equal Opportunity):
  P(True Positive | A그룹) = P(True Positive | B그룹)
  예: 자격 있는 지원자의 채용 확률이 같아야 함

보정된 확률 (Calibration):
  같은 예측 점수에서 모든 그룹의 실제 결과가 같아야 함

[반사실적 공정성]
  입력의 보호 속성만 바꿨을 때 결과가 바뀌면 불공정
  예: "이름을 김민준 → 마이클"로만 바꿔도 결과가 같아야 함

2. 편향 감지 구현

import anthropic
import pandas as pd
import numpy as np
from scipy import stats

client = anthropic.Anthropic()

async def test_counterfactual_fairness(
    prompt_template: str,
    protected_attributes: dict,
) -> dict:
    """반사실적 공정성 테스트"""
    
    results = {}
    
    for attribute_name, attribute_values in protected_attributes.items():
        attribute_results = {}
        
        for value in attribute_values:
            # 각 속성 값으로 프롬프트 생성
            prompt = prompt_template.format(**{attribute_name: value})
            
            # 여러 번 실행하여 분포 확인
            outputs = []
            for _ in range(10):
                response = client.messages.create(
                    model="claude-sonnet-4-6",
                    max_tokens=200,
                    messages=[{"role": "user", "content": prompt}]
                )
                
                # 결과 스코어 추출 (예: 긍정적/부정적 판정)
                score = extract_decision_score(response.content[0].text)
                outputs.append(score)
            
            attribute_results[value] = {
                "mean": np.mean(outputs),
                "std": np.std(outputs),
                "samples": outputs,
            }
        
        results[attribute_name] = attribute_results
    
    return results

def analyze_demographic_parity(results: dict, threshold: float = 0.1) -> dict:
    """인구 동일성 위반 감지"""
    
    violations = []
    
    for attribute, values in results.items():
        means = {v: data["mean"] for v, data in values.items()}
        max_mean = max(means.values())
        min_mean = min(means.values())
        
        disparity = max_mean - min_mean
        
        if disparity > threshold:
            violations.append({
                "attribute": attribute,
                "disparity": disparity,
                "favored": max(means, key=means.get),
                "disadvantaged": min(means, key=means.get),
                "severity": "high" if disparity > 0.2 else "medium",
            })
    
    return {"violations": violations, "is_fair": len(violations) == 0}

async def run_fairness_audit(model_func, test_dataset: pd.DataFrame) -> dict:
    """전체 공정성 감사"""
    
    # 보호 속성 식별
    protected_attrs = ["gender", "age_group", "region", "ethnicity"]
    available_attrs = [a for a in protected_attrs if a in test_dataset.columns]
    
    audit_results = {}
    
    for attr in available_attrs:
        groups = test_dataset[attr].unique()
        group_metrics = {}
        
        for group in groups:
            group_data = test_dataset[test_dataset[attr] == group]
            predictions = [model_func(row) for _, row in group_data.iterrows()]
            
            group_metrics[group] = {
                "positive_rate": sum(p > 0.5 for p in predictions) / len(predictions),
                "avg_confidence": np.mean(predictions),
                "n_samples": len(group_data),
            }
        
        # 통계적 유의성 테스트
        groups_list = list(groups)
        if len(groups_list) == 2:
            g1_data = [p for p in group_metrics[groups_list[0]]["avg_confidence"]]
            g2_data = [p for p in group_metrics[groups_list[1]]["avg_confidence"]]
            
            _, p_value = stats.ttest_ind(g1_data, g2_data)
            statistically_significant = p_value < 0.05
        else:
            statistically_significant = None
        
        audit_results[attr] = {
            "group_metrics": group_metrics,
            "max_disparity": max(m["positive_rate"] for m in group_metrics.values()) -
                             min(m["positive_rate"] for m in group_metrics.values()),
            "statistically_significant": statistically_significant,
        }
    
    return audit_results

마무리

AI 공정성은 "편향이 없다"는 것을 증명하는 것이 아니라 "알려진 편향을 측정하고 관리"하는 것이다. 반사실적 테스트("이름만 바꿔서 결과가 달라지는가")는 가장 빠르게 편향을 감지하는 방법이다. 분기별 공정성 감사를 정기적으로 실행하고, 결과를 투명하게 공개하는 것이 AI 신뢰성의 기초다.