在Github 上面找到了一個信用卡詐騙數據集:
https://github.com/georgymh/ml-fraud-detection
數據源於 kaggle 的信用卡詐騙數據。
其中給出了三種大方法,分別是神經網絡,kmeans,以及邏輯迴歸。
但是今天我想要用XGBoost 試一試。
import sklearn as sk
import xgboost as xgb
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split # 功能:數據集按比例切分爲訓練集和測試集
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.preprocessing import scale # 數據集標準化
import matplotlib.pyplot as plt
from pandas_ml import ConfusionMatrix # 引入混淆矩陣
# https://github.com/pandas-ml/pandas-ml/
df = pd.read_csv('creditcard.csv', low_memory=False)
X = df.iloc[:,:-1] # 基於位置的純整數索引
y = df['Class'] # 類別
X_scaled = scale(X) # 標準化
train_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.35 ,random_state = 12)
dtrain = xgb.DMatrix(train_X, label = train_y)
dtest = xgb.DMatrix(test_X, label = test_y)
param = {
# --------------- Tree Booster 參數 ---------------
'eta':0.3, # 每個循環的學習步長,就是sklearn的learning_rate
'booster':'gbtree', # gbtree使用基於樹的模型進行提升計算,gblinear使用線性模型進行提升計算
'max_depth':5, # 每棵樹的最大深度,默認6
'silent':1, # 默認0,而1表示靜默模式開啓,不會輸出任何信息
# ---------------------------------------------------------------------
'num_class':2, # 類別個數,記得改!
# ---------------------------------------------------------------------
'min_child_weight':1.0, # 決定最小葉子節點樣本權重和
# 'max_leaf_nodes': # 樹上最大的節點或葉子的數量。 可以替代max_depth的作用。
'scale_pos_weight':1.0, # 調節正負樣本不均衡問題
'gamma':0.1, # 這個參數的值越大,算法越保守,默認0
'max_delta_step':0, # 這參數限制每棵樹權重改變的最大步長。0意味着沒有約束。
'subsample':1.0, # 控制對於每棵樹,隨機採樣的比例。
# 減小這個參數的值,算法會更加保守,避免過擬合。
# 如果這個值設置得過小,它可能會導致欠擬合。一般0.5-1
'colsample_bytree':1.0, # 用來控制每棵隨機採樣的列數的佔比(每一列是一個特徵)。一般0.5-1
'colsample_bylevel':1.0, # 控制樹的每一級的每一次分裂,對列數的採樣的佔比。
# 'scale_pos_weight':1, # 在各類別樣本十分不平衡時,把這個參數設定爲一個正值,可以使算法更快收斂
# --------------- Linear Booster 參數 ---------------
# 'lambda_bias':0, # 在偏置上的L2正則
# 'lambda':1, # 權重的L2正則化項。默認1
# 'alpha':1, # 權重的L1正則化項。默認1
# --------------- Task 參數 ---------------
'objective':'multi:softprob', # 定義學習任務及相應的學習目標
#'objective':'reg:linear', # 線性迴歸
#'objective':'reg:logistic', # 邏輯迴歸
#'objective':'binary:logistic', # 二分類的邏輯迴歸問題,輸出爲概率
#'objective':'binary:logitraw', # 二分類的邏輯迴歸問題,輸出結果爲 wTx,wTx指機器學習線性模型f(x)=wTx+b
#'objective':'count:poisson' # 計數問題的poisson迴歸,輸出結果爲poisson分佈
#'objective':'multi:softmax' # 讓XGBoost採用softmax目標函數處理多分類問題,同時需要設置參數num_class
#'objective':'multi:softprob' # 和softmax一樣,但是輸出的是ndata * nclass的向量,
# 可以將該向量reshape成ndata行nclass列的矩陣。
# 每行數據表示樣本所屬於每個類別的概率。
'base_score':0.5, # 所有實例的初始預測評分, global bias
# 'eval_metric'
'seed':0 # 默認爲0
}
num_round = 20 # 循環次數
bst = xgb.train(param, dtrain, num_round) # 參數 + 訓練集 + 循環次數
preds = bst.predict(dtest) # 對測試集作出預測
print("訓練完成")
best_preds = np.asarray([np.argmax(line) for line in preds]) # np.argmax(line) 返回沿着line軸的最大值的索引
# np.asarray 將列表轉換爲數組
# 精確度(Precision):
# P = TP/(TP+FP) ; 反映了被分類器判定的正例中真正的正例樣本的比重
print("精確度(Precision):", precision_score(test_y, best_preds, average='macro')) # 97.32%
print("召回率(Recall):", recall_score(test_y, best_preds, average='macro')) # 90.34%
predicted_y = np.array(best_preds)
right_y = np.array(test_y)
# 混淆矩陣的每一列代表了預測類別,
# 每一列的總數表示預測爲該類別的數據的數目;
# 每一行代表了數據的真實歸屬類別,
# 每一行的數據總數表示該類別的數據實例的數目。
confusion_matrix = ConfusionMatrix(right_y, predicted_y)
# print("Confusion matrix:\n%s" % confusion_matrix)
# confusion_matrix.plot(normalized=True)
# plt.show()
confusion_matrix.print_stats()
'''
population: 99683
P: 176
N: 99507
PositiveTest: 150
NegativeTest: 99533
TP: 142
TN: 99499
FP: 8
FN: 34
TPR: 0.806818181818
TNR: 0.999919603646
PPV: 0.946666666667
NPV: 0.99965840475
FPR: 8.03963540253e-05
FDR: 0.0533333333333
FNR: 0.193181818182
ACC: 0.999578664366
F1_score: 0.871165644172
MCC: 0.87374835762
informedness: 0.806737785464
markedness: 0.946325071417
prevalence: 0.00176559694231
LRP: 10035.5071023
LRN: 0.193197350544
DOR: 51944.3308824
FOR: 0.000341595249817
'''
講道理註釋已經很清楚了,最後的結果也不差。
進一步的問題在於,如何使用類似 SKlearn 中的 balance 參數,讓少數的詐騙1和大多數的非詐騙0均衡一下,讓1的學習權重更重。
這樣的後果就是 False Negative 值降低,但是False Positive 值急劇增高。關鍵看採用哪種策略吧。
目前的研究在於這個
'scale_pos_weight':1.0, # 調節正負樣本不均衡問題
這個參數是 XGBoost 的 SKlearn API中的,如果直接使用XGBoost好像就沒有這個參數?當然也可能是我不明白。反正目前的參數是不錯的。
相對的,在 Github 中的程序有一個線性迴歸的例子就帶有這個 balance 參數,效果很奇妙...