신용카드 사기 검출 분류용 데이터
데이터에 class라는 이름의 컬럼이 사기 유무를 의미
calss 컬럼의 불균형이 극심해서 전체 데이터의 약 0.172%가 1(사기 Fraud)를 가짐
# 1) 데이터 읽기
import pandas as pd
data_path = './14. mini project_creditcard.csv'
raw_data = pd.read_csv(data_path)
raw_data.head()
# 2) 특성
raw_data.columns.values
# 3) 데이터 라벨 확인 (Class : 사기 유무)
raw_data['Class'].value_counts()
Frauds 0.17 % of the dataset
# 5) 데이터 선정
X = raw_data.iloc[:, 1:-1] # Time, Class 컬럼 제외
y = raw_data.iloc[:, -1] # 모든 행의 마지막 컬럼을 선택
X.shape, y.shape
((284807, 29), (284807,))
train_test_split 의 인자들 (https://wikidocs.net/193722)
# 6) 데이터 나누기
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=13, stratify=y)
# 7) 나눈 데이터의 불균형 정도 확인 (y_train 원소의 갯수 세기)
import numpy as np
# 원소의 갯수 세기 : unique + return_counts
# return_counts=True : 각 원소의 중복 갯수가 담긴 배열이 반환/원소가 각각 몇개 존재하는지 확인
tmp = np.unique(y_train, return_counts=True)
tmp, tmp[1], tmp[1]/len(y_train)*100
# 8) 나눈 데이터의 불균형 정도 확인 (y_test 원소의 갯수 세기)
import numpy as np
tmp = np.unique(y_test, return_counts=True)
tmp, tmp[1], tmp[1]/len(y_test)*100 # %를 구한 것
# 1) 분류기 성능 return 함수 설정
from sklearn.metrics import (accuracy_score, precision_score, recall_score, f1_score, roc_auc_score)
def get_clf_eval(y_test, pred):
acc = accuracy_score(y_test, pred) # 정확도 : 정확하게 정답을 맞힌 비중
pre = precision_score(y_test, pred) # 정밀도 : positive 예측치 중 실제 positive 관측치 비중
re = recall_score(y_test, pred) # 재현율 : positive 관측치 중에서 실제로 예측된 비중
f1 = f1_score(y_test, pred) # Precision과 recall의 조화평균(정밀도, 재현율 -> 평균)
auc = roc_auc_score(y_test, pred) # 모델의 성능
return acc, pre, re, f1, auc
from sklearn.metrics import confusion_matrix
def print_clf_eval(y_test, pred):
confusion = confusion_matrix(y_test, pred)
acc, pre, re, f1, auc = get_clf_eval(y_test, pred)
print('=> confusion metrix')
print(confusion)
print('==================')
print('Accuracy : {0:.4f}, Precision : {1:.4f} '.format(acc, pre))
print('Recall : {0:.4f}, F1 : {1:.4f}, AUC : {2:.4f} '.format(re, f1, auc))
# (https://coduking.com/entry/ROC-curve-AUC-%EA%B0%9C%EB%85%90-%EB%B0%8F-sklearn-%EC%BD%94%EB%93%9C)
# (https://coduking.com/entry/%EB%B6%84%EB%A5%98%EB%AC%B8%EC%A0%9C-%EC%84%B1%EB%8A%A5%ED%8F%89%EA%B0%80-%EC%A7%80%ED%91%9C-Accuracy-Recall-Precision-F1-score-titanic-%EC%8B%A4%EC%8A%B5)
# 2) Logistic Regression
from sklearn.linear_model import LogisticRegression
lr_clf = LogisticRegression(random_state=13, solver='liblinear')
lr_clf.fit(X_train, y_train)
lr_pred = lr_clf.predict(X_test)
print_clf_eval(y_test, lr_pred)
Accuracy가 99.92%로 보이지만,
실제 1중에서 몇개를 맞췄는지 보는 Recall 의 값이 59%에 불과함
-> Fraud 검출을 못했다고 봐야 함
-> 더 성능을 끌어 올려야 함
# 3) Decision Tree
from sklearn.tree import DecisionTreeClassifier
dt_clf = DecisionTreeClassifier(random_state=13, max_depth=4)
dt_clf.fit(X_train, y_train)
dt_pred = dt_clf.predict(X_test)
print_clf_eval(y_test, dt_pred)
DecisionTreeClassifier 의 결과는 106개 중 42개가 틀렸고 71.62% 로 나옴.
이전 보다 높음
# 4) Random Forest
from sklearn.ensemble import RandomForestClassifier
rf_clf = RandomForestClassifier(random_state=13, n_jobs=-1, n_estimators=100)
rf_clf.fit(X_train, y_train)
rf_pred = rf_clf.predict(X_test)
print_clf_eval(y_test, rf_pred)
recall이 조금더 올라감.
이전보다 덜 틀린 38개
# 5) LightGBM
from lightgbm import LGBMClassifier
lgbm_clf = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1, boost_from_average=False)
lgbm_clf.fit(X_train, y_train)
lgbm_pred = lgbm_clf.predict(X_test)
print_clf_eval(y_test, lgbm_pred)
성능이 조-금 좋아진 느낌.
# 1) 모델, 데이터를 주고 성능을 출력하는 함수
def get_result(model, X_train, y_train, X_test, y_test):
model.fit(X_train, y_train)
pred = model.predict(X_test)
return get_clf_eval(y_test, pred)
# 2) 여러개 모델의 성능을 정리 -> DataFrame 반환
def get_result_pd(models, model_names, X_train, y_train, X_test, y_test):
col_name = ['accuracy', 'precision', 'recall', 'f1', 'roc_auc']
tmp = []
for model in models:
tmp.append(get_result(model, X_train, y_train, X_test, y_test))
return pd.DataFrame(tmp, columns=col_name, index=model_names)
# 3) 4개의 분류모델 > 표 (정리)
import time
models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
model_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']
start_time = time.time()
results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)
print('Fit time : ', time.time() - start_time)
results
# 1) raw_data의 Amount 컬럼 확인
plt.figure(figsize=(10,5))
sns.distplot(raw_data['Amount'], color='b')
plt.show()
raw_data['Amount'].values
array([149.62, 2.69, 378.66, ..., 67.88, 10. , 217. ])
raw_data['Amount'].values.reshape(-1,1)
# reshape : https://domybestinlife.tistory.com/149
raw_data.iloc[:, 1:-2] # Time, Amount, Class 삭제
StandardScaler 를 통해,
'Amount'가 몰려있는 상태의 편형성을 바꿔보고 싶음
# 2) Amount + StandardScaler
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
amount_n = scaler.fit_transform(raw_data['Amount'].values.reshape(-1,1))
raw_data_copy = raw_data.iloc[:, 1:-2]
raw_data_copy['Amount_Scaler'] = amount_n # StandardScaler 학습한 컬럼 생성
raw_data_copy.head()
# 3) 데이터 나누기 >> 재평가
X_train, X_test, y_train, y_test = train_test_split(raw_data_copy, y, test_size=0.3, random_state=13, stratify=y)
models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
model_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']
start_time = time.time()
results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)
print('Fit time : ', time.time() - start_time)
results
models 안에 model이 넘어올건데,
그 모델에다가 predict를 시키고, predict_proba를 X_test에 대해서 시켜 줌
왜? ROC 커브를 그리려면 '확률값(predict_proba)'이 있어야 하기 때문
대각선 그리는 방법 : plt.plot([0,1], [0,1], 'k--', label='random quess')
# 4) 모델별 ROC 커브
from sklearn.metrics import roc_curve
def draw_roc_curve(models, model_names, X_test, y_test):
plt.figure(figsize=(10,10))
for model in range(len(models)):
pred = models[model].predict_proba(X_test)[:, 1]
fpr, tpr, thresholds = roc_curve(y_test, pred)
plt.plot(fpr, tpr, label=model_names[model])
# 대각선
plt.plot([0,1], [0,1], 'k--', label='random quess')
plt.title('ROC')
plt.legend()
plt.grid()
plt.show()
# draw_roc_curve(models, model_names, X_test, y_test)
from sklearn.metrics import roc_curve
def draw_roc_corve(models,model_names, X_test, y_test):
plt.figure(figsize=(10, 10))
for model in range(len(models)):
pred = models[model].predict_proba(X_test)[:, 1]
fpr, tpr, thresholds = roc_curve(y_test, pred)
plt.plot(fpr, tpr, label=model_names[model])
plt.plot([0, 1], [0, 1], 'k--', label='random quess')
plt.title('ROC')
plt.legend()
plt.grid()
plt.show()
draw_roc_corve(models, model_names, X_test, y_test)
log 함수를 적용해 보겠음!
log 함수 : 높은 값은 상대적으로 낮게 잡아주고 낮은 값은 그대로 사용
# 5) log scale 확인
amount_log = np.log1p(raw_data['Amount'])
raw_data_copy['Amount_Scaler'] = amount_log
raw_data_copy.head()
# 6) 분포(displot) 확인
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(10,5))
sns.distplot(raw_data_copy['Amount_Scaler'], color='r')
plt.show()
# 7) 성능 확인
X_train, X_test, y_train, y_test = train_test_split(raw_data_copy, y, test_size=0.3, random_state=13, stratify=y)
start_time = time.time()
results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)
print('Fit time : ', time.time() - start_time)
results
# 1) 특이한 데이터 확인
import seaborn as sns
plt.figure(figsize=(10,7))
sns.boxplot(data=raw_data[['V13', 'V14', 'V15']]);
# 2) Outlier를 정리하기 위해 Outlier의 인덱스를 파악하는 코드
def get_outlier(df=None, column=None, weight=1.5):
fraud = df[df['Class']==1][column]
# 25% 지점
quantile_25 = np.percentile(fraud.values, 25)
# 75% 지점
quantile_75 = np.percentile(fraud.values, 75)
iqr = quantile_75 - quantile_25
iqr_weight = iqr * weight # (weight = 1.5)
lowest_val = quantile_25 - iqr_weight
highest_val = quantile_75 + iqr_weight
# 제거할 outlier_index 를 설정
outlier_index = fraud[(fraud < lowest_val) | (fraud > highest_val)].index
return outlier_index
# 3) 파악하는 코드 작성했으니, Outlier 찾기
get_outlier(df=raw_data, column='V14', weight=1.5)
Index([8296, 8615, 9035, 9252], dtype='int64')
# 4) Outlier 제거 전에 전체 개수 확인
raw_data_copy.shape
(284807, 29)
# 5) Outlier 제거
outlier_index = get_outlier(df=raw_data, column='V14', weight=1.5)
raw_data_copy.drop(outlier_index, axis=0, inplace=True) # 행제거 (axis=0)
raw_data_copy.shape
(284803, 29)
# 6) Outlier 제거 후 데이터 다시 나누기 >> 재평가
X = raw_data_copy
raw_data.drop(outlier_index, axis=0, inplace=True)
y = raw_data.iloc[:, -1]
X_train, X_test, y_train, y_test = train_test_split(raw_data_copy, y, test_size=0.3, random_state=13, stratify=y)
models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
model_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']
start_time = time.time()
results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)
print('Fit time : ', time.time() - start_time)
results
!pip install imbalanced-learn