현대 금융 거래에서는 다양한 형태의 사기(fraud)가 끊임없이 발생하고 있습니다. 다행히 머신러닝 알고리즘을 활용하면 과거의 거래 데이터를 학습시켜 새로운 거래가 사기일 가능성을 예측할 수 있습니다.
이 글에서는 실제 신용카드 거래 데이터를 예제로 사용합니다. 데이터를 분석하고, 특성(feature)과 레이블(label)을 생성한 뒤, 머신러닝 알고리즘 중 하나를 적용하여 해당 거래가 사기인지 아닌지를 판별해 보겠습니다. 마지막에는 선택한 모델의 정확도(accuracy), 정밀도(precision), F-점수(F-score)를 계산하여 모델 성능을 종합적으로 평가합니다.
1단계: 데이터 준비
먼저 원본 데이터를 읽어 들이고, 포함된 변수들을 살펴보며 샘플 데이터를 확인합니다. 이 과정을 통해 데이터셋에 어떤 컬럼들이 존재하는지 파악하고 각 특성을 이해할 수 있습니다. 여기서는 Pandas 라이브러리를 사용해 데이터프레임을 생성하며, 이후 단계에서도 이 데이터프레임을 활용하게 됩니다.
예제 코드
import pandas as pd
# pandas로 creditcard.csv 파일 불러오기
datainput = pd.read_csv('E:\\creditcard.csv')
# https://www.kaggle.com/mlg-ulb/creditcardfraud
# 상위 5개 레코드 출력
print(datainput[0:5],"\n")
# 전체 데이터셋의 크기(shape) 출력
print("Shape of Complete Data Set")
print(datainput.shape,"\n")실행 결과
위 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
Time V1 V2 V3 ... V27 V28 Amount Class 0 0.0 -1.359807 -0.072781 2.536347 ... 0.133558 -0.021053 149.62 0 1 0.0 1.191857 0.266151 0.166480 ... -0.008983 0.014724 2.69 0 2 1.0 -1.358354 -1.340163 1.773209 ... -0.055353 -0.059752 378.66 0 3 1.0 -0.966272 -0.185226 1.792993 ... 0.062723 0.061458 123.50 0 4 2.0 -1.158233 0.877737 1.548718 ... 0.219422 0.215153 69.99 0 [5 rows x 31 columns] Shape of Complete Data Set (284807, 31)
이 데이터셋은 총 284,807개의 거래와 31개의 컬럼으로 구성되어 있습니다. V1~V28은 PCA(주성분 분석)로 변환된 익명화된 특성이며, Time과 Amount는 각각 거래 시간과 거래 금액, Class는 사기 여부(0: 정상, 1: 사기)를 나타냅니다.
2단계: 데이터 불균형 확인
다음으로 사기 거래와 정상 거래가 각각 얼마나 분포되어 있는지 확인합니다. 이를 통해 전체 데이터 중 사기 거래가 차지하는 비율을 가늠할 수 있습니다. 머신러닝에서는 이를 데이터 불균형(data imbalance)이라고 부릅니다. 대부분의 거래가 정상이라면 소수의 사기 거래를 정확히 판별하는 것이 매우 어려워지기 때문입니다.
Class 컬럼을 사용해 사기 거래 건수를 집계하고, 실제 사기 거래 비율을 계산해 보겠습니다.
예제 코드
import pandas as pd
# pandas로 creditcard.csv 파일 불러오기
datainput = pd.read_csv('E:\\creditcard.csv')
false = datainput[datainput['Class'] == 1]
true = datainput[datainput['Class'] == 0]
n = len(false)/float(len(true))
print(n)
print('False Detection Cases: {}'.format(len(datainput[datainput['Class'] == 1])))
print('True Detection Cases: {}'.format(len(datainput[datainput['Class'] == 0])),"\n")실행 결과
0.0017304750013189597 False Detection Cases: 492 True Detection Cases: 284315
전체 284,807건 중 사기 거래는 단 492건(약 0.17%)에 불과합니다. 이처럼 극심한 클래스 불균형이 존재하므로, 단순 정확도만으로는 모델 성능을 판단하기 어렵습니다. 이것이 바로 이후 정밀도와 재현율 같은 추가 지표가 필요한 이유입니다.
3단계: 거래 유형별 상세 통계
사기 거래와 정상 거래 각각의 성격을 더 깊이 파악하기 위해 통계적 추정을 수행합니다. 평균, 표준편차, 최댓값, 최솟값, 그리고 다양한 백분위수(percentile)를 확인할 수 있으며, 이는 Pandas의 describe() 메서드로 손쉽게 구현됩니다.
예제 코드
import pandas as pd
# pandas로 creditcard.csv 파일 불러오기
datainput = pd.read_csv('E:\\creditcard.csv')
# 데이터 불균형 확인
false = datainput[datainput['Class'] == 1]
true = datainput[datainput['Class'] == 0]
# 사기 거래(False Detection) 건수
print("False Detection Cases")
print("----------------------")
print(false.Amount.describe(),"\n")
# 정상 거래(True Detection) 건수
print("True Detection Cases")
print("----------------------")
print(true.Amount.describe(),"\n")실행 결과
False Detection Cases ---------------------- count 492.000000 mean 122.211321 std 256.683288 min 0.000000 25% 1.000000 50% 9.250000 75% 105.890000 max 2125.870000 Name: Amount, dtype: float64 True Detection Cases ---------------------- count 284315.000000 mean 88.291022 std 250.105092 min 0.000000 25% 5.650000 50% 22.000000 75% 77.050000 max 25691.160000 Name: Amount, dtype: float64
흥미로운 점은 사기 거래의 평균 금액(약 122달러)이 정상 거래의 평균 금액(약 88달러)보다 높다는 것입니다. 또한 사기 거래의 중앙값(9.25달러)이 정상 거래(22달러)보다 낮아, 소액 결제 반복 시도 등의 사기 패턴을 엿볼 수 있습니다.
4단계: 특성(X)과 레이블(Y) 분리
머신러닝 알고리즘을 구현하기 전에 종속 변수와 독립 변수를 구분해야 합니다. 즉, 어떤 컬럼이 예측 대상(레이블)이고 어떤 컬럼들이 예측에 사용되는 입력값(특성)인지 결정하는 작업입니다.
이 데이터셋에서 Class 컬럼은 나머지 모든 컬럼에 의존하는 값이므로, 마지막 컬럼(Class)을 위한 데이터프레임과 나머지 컬럼들을 위한 데이터프레임을 각각 생성합니다. 이 두 데이터프레임은 앞으로 만들 모델을 학습시키는 데 사용됩니다.
예제 코드
import pandas as pd
# pandas로 creditcard.csv 파일 불러오기
datainput = pd.read_csv('E:\\creditcard.csv')
# 특성(X)과 레이블(y) 분리
# 모든 행에 대해 마지막 컬럼을 제외한 나머지 선택
X = datainput.iloc[:, :-1].values
# 모든 행의 마지막 컬럼 선택
Y = datainput.iloc[:, -1].values
print(X.shape)
print(Y.shape)실행 결과
(284807, 30) (284807,)
5단계: 모델 학습을 위한 데이터 분할
이제 데이터셋을 학습용(train)과 테스트용(test) 두 부분으로 나눕니다. test_size 파라미터는 전체 데이터 중 테스트에만 사용할 비율을 결정합니다. 여기서는 20%(0.2)를 테스트 데이터로 할당했습니다. 이렇게 분할하면 생성된 모델이 처음 보는 데이터에 대해서도 잘 작동하는지 검증할 수 있어, 모델에 대한 신뢰도를 확보하는 데 도움이 됩니다.
예제 코드
import pandas as pd
from sklearn.model_selection import train_test_split
# pandas로 creditcard.csv 파일 불러오기
datainput = pd.read_csv('E:\\creditcard.csv')
# 특성(X)과 레이블(y) 분리
X = datainput.iloc[:, :-1].values
# 모든 행의 마지막 컬럼 선택
Y = datainput.iloc[:, -1].values
# train_test_split 메서드 적용
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2)6단계: 결정 트리 분류기 적용
이 문제에 적용할 수 있는 알고리즘은 매우 다양하지만, 여기서는 결정 트리(Decision Tree)를 분류 알고리즘으로 선택합니다. 트리의 최대 깊이(max_depth)를 4로 설정하여 과적합(overfitting)을 방지하고, 테스트 샘플을 공급해 예측값을 산출합니다. 마지막으로 테스트 결과의 정확도를 계산하여 이 알고리즘을 계속 사용할지 판단합니다.
예제 코드
import pandas as pd
from sklearn import metrics
from sklearn.model_selection import train_test_split
# pandas로 creditcard.csv 파일 불러오기
datainput = pd.read_csv('E:\\creditcard.csv')
# 특성(X)과 레이블(y) 분리
X = datainput.iloc[:, :-1].values
Y = datainput.iloc[:, -1].values
# train_test_split 메서드 적용
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2)
# DecisionTreeClassifier 적용
from sklearn.tree import DecisionTreeClassifier
classifier=DecisionTreeClassifier(max_depth=4)
classifier.fit(X_train,Y_train)
predicted=classifier.predict(X_test)
print("\npredicted values :\n",predicted)
# 정확도(Accuracy) 계산
DT = metrics.accuracy_score(Y_test, predicted) * 100
print("\nThe accuracy score using the DecisionTreeClassifier : ",DT)실행 결과
predicted values : [0 0 0 ... 0 0 0] The accuracy score using the DecisionTreeClassifier : 99.9367999719111
정확도가 무려 99.94%나 나왔지만, 앞서 확인했듯이 정상 거래가 99.83%를 차지하므로 모든 거래를 '정상'으로 예측해도 비슷한 정확도가 나옵니다. 따라서 더 정교한 평가 지표가 반드시 필요합니다.
7단계: 평가 지표 산출 — 정밀도, 재현율, F-점수
위 단계에서 정확도가 만족스러운 수준이라면, 다양한 파라미터를 통해 모델을 한층 더 심층 평가합니다. 여기서는 정밀도(Precision), 재현율(Recall), F-점수(F-score)를 평가 지표로 사용합니다.
- 정밀도(Precision): 모델이 사기라고 예측한 거래 중 실제 사기인 거래의 비율입니다. 즉, 검색된 인스턴스 중 관련 있는 인스턴스의 비율을 의미합니다.
- 재현율(Recall): 실제 사기 거래 전체 중 모델이 사기로 올바르게 탐지한 거래의 비율입니다.
- F-점수(F-score): 정밀도와 재현율의 조화평균으로, 두 지표의 균형을 하나의 숫자로 요약해 줍니다.
예제 코드
import pandas as pd
from sklearn import metrics
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# pandas로 creditcard.csv 파일 불러오기
datainput = pd.read_csv('E:\\creditcard.csv')
# 특성(X)과 레이블(y) 분리
X = datainput.iloc[:, :-1].values
Y = datainput.iloc[:, -1].values
# train_test_split 메서드 적용
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2)
# DecisionTreeClassifier 적용
from sklearn.tree import DecisionTreeClassifier
classifier=DecisionTreeClassifier(max_depth=4)
classifier.fit(X_train,Y_train)
predicted=classifier.predict(X_test)
print("\npredicted values :\n",predicted)
#
# #정확도(Accuracy)
DT = metrics.accuracy_score(Y_test, predicted) * 100
print("\nThe accuracy score using the DecisionTreeClassifier : ",DT)
#
# #정밀도(Precision)
print('precision')
# Precision = TP / (TP + FP) (TP = True Positive, TN = True Negative, FP = False Positive, FN = False Negative)
precision = precision_score(Y_test, predicted, pos_label=1)
print(precision_score(Y_test, predicted, pos_label=1))
#재현율(Recall)
print('recall')
# Recall = TP / (TP + FN)
recall = recall_score(Y_test, predicted, pos_label=1)
print(recall_score(Y_test, predicted, pos_label=1))
#F1-점수(f1-score)
print('f-Score')
# F-점수는 정밀도와 재현율을 모두 고려하여 정확도를 측정하는 통계적 방법입니다.
fscore = f1_score(Y_test, predicted, pos_label=1)
print(f1_score(Y_test, predicted, pos_label=1))실행 결과
The accuracy score using the DecisionTreeClassifier : 99.9403110845827 precision 0.810126582278481 recall 0.7710843373493976 f-Score 0.7901234567901234
결론 및 개선 방향
결정 트리 모델(max_depth=4)의 평가 결과는 다음과 같습니다.
- 정확도: 약 99.94%
- 정밀도: 약 81.01% — 사기로 예측한 거래 중 실제 사기인 비율
- 재현율: 약 77.11% — 실제 사기 거래 중 탐지에 성공한 비율
- F-점수: 약 79.01%
정확도는 매우 높지만, 실질적인 사기 탐지 성능을 보여주는 정밀도와 재현율은 개선의 여지가 있습니다. 실무에서는 SMOTE 같은 오버샘플링 기법으로 클래스 불균형을 완화하거나, 랜덤 포레스트(Random Forest), XGBoost 같은 앙상블 모델을 활용하고, 하이퍼파라미터 튜닝을 통해 재현율을 높이는 방향으로 모델을 발전시킬 수 있습니다. 특히 금융 사기 탐지에서는 놓치는 사기 거래(FN)의 비용이 크므로 재현율을 우선적으로 고려하는 것이 일반적입니다.