HDLSS 데이터로부터 발생하는 이슈
현재 일하고 있는 의료/헬스케어 도메인에서는 기업들이 보다 전문화된 문제를 해결하고자 하는 경향이 있다(뇌졸중 환자의 뇌 MRI 데이터를 활용해 질병 발생 여부를 예측하려 한다거나, 치매의 이전 단계인 경도인지장애 단계를 의사보다 정확하게 진단하고자 한다거나). 이런 문제를 풀기 위해 필요한 데이터는 상급병원에 몰려있을 테고, 상급병원에서는 다양한 장비와 전문 인력을 바탕으로 소수의 환자로부터 다양한 데이터를 추출하게 된다. 이러한 상황에서, feature는 많지만 sample은 적은, High-dimension and low-sample-size (HDLSS) data가 만들어진다. 추측컨대 의료/헬스케어 도메인이 아니더라도, 풀고자하는 문제가 specific하면 할수록 HDLSS data를 활용할 가능성이 높을 것 같다.
HDLSS data을 활용해 ML 모델을 만들게 되면 아래와 같은 이슈들이 동반된다.
- 훈련 당시에는 훈련 자체가 잘 안된다. 데이터를 그대로 사용했다간 모델이 과적합되기 일쑤이고, 최적 파라미터를 찾기도 어렵다.
- 만들어진 모델이 신뢰성 있는 모델인지 확인해야 한다. 애초에 샘플이 적다면, test set에서의 성능을 100% 신뢰하기 어렵다.
일반적으로, ‘훈련 자체가 잘 안되’는 문제는(비교적 널리 알려진) 아래의 방법들을 사용해 해결해 볼 수 있다.
- Cross validation
- Normalization
- Dimensionality reduction
- Feature selection
- Augmentation
- Sparse learning
- Ensemble
- …
ML 모델 검증
하지만 만들어진 모델이 ‘신뢰성 있는 모델인지’ 검증하려면 어떻게 해야할까? 누군가가 “이 모델 정말 믿을만 한 모델이에요?” 하고 물어봤을 때, “데이터는 부족하지만 이러이러한 방법들을 적용했으니 괜찮아요” 하고 대답하면 그만인걸까? 당연히 아니다. 이 질문에 답하기 위한 지표가 필요하다.
1) Train-test AUC 비교
이상적으로는, real-world data에 대한 예측이 test set에 대한 예측과 비슷할 때 신뢰할 만한 모델이라고 말할 수 있을 것이다. 그러나 이는 모델을 real-world data에 적용시켜 본 이후에나 판단할 수 있기 때문에, 대안적으로 고안된 방법이 ML 모델링에서 당연하다는 듯이 적용되는 train-test 분리 방법이다. 이미 관측된 데이터를 활용해 아직 관측되지 않은 데이터를 예측하는 것이 ML 모델링의 목표이므로, 모델 훈련에 사용되지 않은(아직 관측되지 않았다고 가정한) test data를 잘 예측할 수 있다면 모델이 일반적인 상황에 적용될 수 있다고 생각하는 것이다.
여기에서 첫 번째 검증 방법을 생각해볼 수 있다. train 결과와 test 결과의 차이가 크지 않은지 검증하는 것이다(모델이 과적합되지 않았는지 확인하는 것). 간단하게는 validation set, test set에서의 여러 성능 지표들을 비교하는 방법도 있고, dataset 비율(X)에 따른 train-test AUC 값의 차이(Y축)를 그래프로 그려 시각화해봤을 때 train AUC와 test AUC의 차이가 X값에 상관없이 0에 가깝고, 두 AUC가 모두 충분히 높다면 이상적인 검증결과를 보인다고 할 수 있다.
2) Perturbation method
조금 다른 측면에서 생각해보자. real-world data는 정제되지도 않고, 결측값도 많은 데이터인 경우가 대부분이다. 물론 이 데이터를 모델에 넣기 전 전처리를 적용하기는 하겠지만, 그럼에도 훈련에 사용된 데이터와 비교하면 상대적으로 지저분한 데이터일 가능성이 높다.
여기에서 파생되는 두 번째 검증 방법은 input data에 일부 noise가 들어가더라도 모델 예측력이 크게 떨어지지 않는지 확인해보는 것이다. data에 noise를 의도적으로 추가하는 것을 ‘Perturbation’이라고 한다. Noise data 비율이 높아도 AUC가 덜 떨어지는 모델이 robust한 모델이라고 생각할 수 있다.
Perturbation은 raw value 기반으로 적용할 수도 있고, quantile 기반으로 적용해볼 수도 있다.
- Raw Perturbation: 데이터에 노이즈를 추가하여, 모델이 이러한 변형된 데이터에 대해 얼마나 잘 작동하는지 평가할 수 있다. 일반적으로 평균이 0이고 표준편차가 σ인 가우시안 분포로부터 생성된 노이즈를 적용한다.
- Quantile Perturbation: 데이터의 분위수(quantile)를 기반으로 데이터를 변형한다. 가우시안 분포에 비해 비대칭적인 데이터나 아웃라이어 값이 많을 때 안정적인 Perturbation이 가능하다.
Perturbation 구현 코드
# Raw Perturbation def raw_perturbation(X, perturb_size=0.1): X_perturbed = X.copy() for col in X.columns: if np.issubdtype(X[col].dtype, np.number): std = X[col].std() noise = np.random.normal(0, perturb_size * std, size=X.shape[0]) X_perturbed[col] += noise return X_perturbed # Quantile Perturbation def quantile_perturbation(X, perturb_size=0.1): X_perturbed = X.copy() for col in X.columns: if np.issubdtype(X[col].dtype, np.number): sorted_vals = np.sort(X[col].unique()) quantiles = X[col].rank(method='max', pct=True) noise = np.random.uniform(-perturb_size/2, perturb_size/2, size=X.shape[0]) quantiles_perturbed = quantiles + noise quantiles_perturbed = np.clip(quantiles_perturbed, 0, 1) X_perturbed[col] = np.interp( quantiles_perturbed, np.linspace(0, 1, len(sorted_vals)), sorted_vals ) return X_perturbed # 모델 평가 함수 def evaluate_models(models, model_names, X_test, y_test, perturb_func, perturb_size=0.1, n_iterations=10): results = {name: {metric: [] for metric in ['accuracy', 'f1', 'auc', 'recall', 'specificity']} for name in model_names} for i in range(n_iterations): X_test_perturbed = perturb_func(X_test, perturb_size=perturb_size) for model, name in zip(models, model_names): metrics = evaluate_model(model, X_test_perturbed, y_test) for metric_name, value in metrics.items(): results[name][metric_name].append(value) return results # Raw Perturbation results_raw = evaluate_models( models=models, model_names=model_names, X_test=X_test, y_test=y_test, perturb_func=raw_perturbation, perturb_size=perturb_size, n_iterations=n_iterations ) # Quantile Perturbation results_quantile = evaluate_models( models=models, model_names=model_names, X_test=X_test, y_test=y_test, perturb_func=quantile_perturbation, perturb_size=perturb_size, n_iterations=n_iterations )
마무리
이미 만들어진 ML 모델을 real world data에 적용해보기 전에, 어떤 식으로 검증해볼 수 있을까 고민하다 Perturbation이라는 키워드를 알게 되어서 간단히 코드 구현까지 해보았다. 구글링을 하다보니 최근 모델의 robustness를 검증하는 방법에 대한 논문들이 조금씩 나오고 있어서, 좀 더 자세히 공부해 정리해두고자 한다.
References
Further Reading
Share article