機器學習中欠採樣+ Logistic迴歸—不平衡的數據

機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

為了打擊欺詐,我們必須首先檢測它。在發現欺詐行為時,您必須考慮:

  • 如果你試圖找出所有的欺詐案件,其中一些案件將被錯誤的貼上標籤。這將導致無辜的人被指控犯有欺詐罪。
  • 如果你試圖讓無辜的人免受指控,你就會把一些欺詐者誤認為是無辜的。在這種情況下,公司將損失更多的錢。

您的欺詐檢測算法不完善是不可避免的。

我們來看看這些數據。

它是貨幣交易的數據集。它給出了發送者的ID,接收者的ID,轉移的金額,以及交易前後的發送者和接收者的餘額。它還告訴我們哪些樣本是欺詐,哪些不是。它是生成的數據集。公司不希望你知道他們損失了多少錢,所以我們只能這麼做。

讓我們加載數據集並查看它的樣子:

import pandas as pd 
cols = ['step', 'type', 'amount', 'nameOrig', 'oldbalanceOrg', 'newbalanceOrig',
'nameDest', 'oldbalanceDest', 'newbalanceDest', 'isFraud', 'isFlaggedFraud']
df = pd.read_csv('PS_20174392719_1491204439457_log.csv', header = 0, names = cols)
print('df.shape:', df.shape)

df.head()
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

我們可以將表拆分成不同的集合。每組都具有所有特徵,但不是所有的觀察結果。

對於訓練集,我們可以使用isFraud列值來訓練我們的模型。在測試集上應用模型將為我們提供每個觀察的預測isFraud值。

一種簡單的方法可能是嘗試:

pd.value_counts(df.isFraud, normalize = True)
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

我們可以這樣做的另一種方法是使用isFraud列的模式:

import numpy as np
from sklearn.metrics import accuracy_score
majority_class = df.isFraud.mode()[0]
y_pred = np.full(shape = df.isFraud.shape, fill_value = majority_class)
accuracy_score(df.isFraud, y_pred)
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

這給我們的準確度得分值為0.998709,與value_counts()相同 - 這是預期的。isFraud=0的值要比isFraud=1的值多得多。我們可以從上面的value_counts()或下面輸出的分類報告中看到這一點:

from sklearn.metrics import classification_report
print(classification_report(df.isFraud, y_pred))
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

我們可以看到我們獲得了完美的召回率和精確度,但support值告訴我們另一件事情。我們有6354407個值支持isFraud = 0的情況,而8213個值支持isFraud = 1。

讓我們來看看ROC曲線的AUC評分,而不是準確性。該分數衡量我們的模型區分類的能力。

from sklearn.metrics import roc_auc_score
roc_auc_score(df.isFraud, y_pred)
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

0.5 

這給了我們0.5的值。ROC AUC評分的值為1.0,是任何人使用任何模型都能得到的最佳值。為什麼我們得到0.5?這是因為我們可以完美地預測所有isFraud = 0的情況,但是沒有一個isFraud = 1的情況。所以在這兩個類中,我們只能預測1(這給我們的ROC AUC為0.5)。

為了為我們的模型提供公平的競爭環境,我們可以對欺詐交易進行過度抽樣,也可以對乾淨的交易進行抽樣。我們可以使用imbalance-learn庫來完成這項工作。

from imblearn.under_sampling import RandomUnderSampler
X = df.drop(['isFraud', 'type', 'nameOrig', 'nameDest'], axis = 1)
y = df.isFraud
rus = RandomUnderSampler(sampling_strategy=0.8)
X_res, y_res = rus.fit_resample(X, y)
print(X_res.shape, y_res.shape)
print(pd.value_counts(y_res))
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

我們將隨機下采樣的sampling_strategy設置為0.8。這只是為了說明我們這樣做時會發生什麼。它允許我們指定少數類樣本與多數類樣本的比率。它為我們提供了18479行數據,其值計數如下:

(18479, 7) (18479,)
0 10266
1 8213
dtype: int64

讓我們看看我們的表在重採樣和刪除這些列之後是什麼樣子的:

cols_numeric = ['step', 'amount', 'oldbalanceOrg', 'newbalanceOrig',
'oldbalanceDest', 'newbalanceDest', 'isFlaggedFraud']
df_rus = pd.DataFrame(X_res, columns = cols_numeric)
df_rus.head()
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

現在讓我們將數據集分成3個部分 - 訓練,驗證和測試數據集。驗證數據集我們可以反覆使用不同的模型。一旦我們認為我們有最好的模型,我們將使用我們的測試數據集。

我們這樣做的原因是,我們的模型不僅應該用部分訓練數據集為我們提供良好的結果,而且還應該用我們從未見過的數據提供良好的結果。通過將測試數據集只使用一次,我們強迫自己不要過度使用驗證數據集。

trainize / valsize / testsize顯示了應保留用於訓練/驗證/測試的總數據集的分數。

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
def train_validation_test_split(
X, y, train_size=0.8, val_size=0.1, test_size=0.1,
random_state=None, shuffle=True):
assert int(train_size + val_size + test_size + 1e-7) == 1
X_train_val, X_test, y_train_val, y_test = train_test_split(
X, y, test_size=test_size, random_state=random_state, shuffle=shuffle)
X_train, X_val, y_train, y_val = train_test_split(
X_train_val, y_train_val, test_size=val_size/(train_size+val_size),
random_state=random_state, shuffle=shuffle)
return X_train, X_val, X_test, y_train, y_val, y_test
X_train, X_val, X_test, y_train, y_val, y_test = train_validation_test_split(
X_res, y_res, train_size=0.8, val_size=0.1, test_size=0.1, random_state=1)
class_weight = {0: 4, 1: 5}
model = LogisticRegression(class_weight=class_weight)
model.fit(X_train, y_train)
y_pred = model.predict(X_val)
print(classification_report(y_val, y_pred))
print('accuracy', accuracy_score(y_val, y_pred))
roc_auc_score(y_val, y_pred)
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

注意class_weight參數。我們把它放在那裡是因為isFraud = 0的欠採樣行數為10000行,isFraud = 1的欠採樣行數為8000行。我們想要權衡它們以使它們平衡。這樣做的比例是4:5,這是這裡使用的類權重。

如果我們在沒有設置sampling_strategy = 0.8的情況下進行了欠採樣,那麼我們就會有平衡的類,並且不需要class_weight參數。如果我們得到一個新的數據集,它的參數稍微不平衡,我們可以使用帶有類權重的邏輯迴歸來平衡它,而不需要重新採樣。

機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

現在我們得到了0.90的準確度 - 這是一個很好的分數。我們的ROC AUC評分也是0.9。

現在讓我們在測試數據集上嘗試我們的模型:

y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred))
print('Accuracy', accuracy_score(y_test, y_pred))
print('ROC AUC score:', roc_auc_score(y_test, y_pred))
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

再次得到0.90。看起來RandomUnderSampler做得很好。

我們必須將模型應用於完整的(未採樣的)數據集。我們接下來就這樣做:

y_pred = model.predict(X)
print(classification_report(y, y_pred))
print('Accuracy:', accuracy_score(y, y_pred))
print('ROC AUC score:', roc_auc_score(y, y_pred))
機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

機器學習中欠採樣+ Logistic迴歸—不平衡的數據 - 欺詐檢測

準確率和ROC AUC評分都非常好,isFraud = 0的精度/召回率/f1-score也是如此。問題是isFraud = 1的精度非常低,為0.01。由於f1-score是精確度和召回率的加權平均值,因此它也低至0.02。也許我們這裡沒有足夠的數據來處理Logistic迴歸。或者我們應該進行過採樣而不是欠採樣。


分享到:


相關文章: