Explainable Transaction Anomaly Detector
不要只告訴分析師「這筆交易分數很高」;要讓他知道交易偏離了什麼歷史行為、哪些統計證據同時成立,以及接下來該查什麼。
先用一句話理解這個專案
它把「這筆交易看起來不尋常」拆成一組分析師能核對的統計證據與原因代碼,並用時間切分的合成標籤檢查規則到底抓對多少、漏掉多少。
不是犯罪分類器,也不是自動封鎖帳戶的系統。異常只代表行為偏離歷史基準,可能是旅行、換裝置、薪資入帳或其他合理原因。
專案目的:讓異常警示可以被理解、挑戰與重現
交易風控真正困難的地方,往往不是產生一個高分,而是回答後續問題:為什麼是這筆?和這個帳戶平常的行為差多少?是金額、頻率、裝置還是地理位置造成?如果門檻改變,案件量會怎麼變?
如果系統只輸出一個黑箱分數,分析師無法快速判斷它是否合理,稽核人員也難以追溯,工程師更不知道誤報應該從哪一條規則開始修。這個專案的目的,就是建立一條從原始交易、帳戶歷史、統計證據到人工警示的透明路徑。
因此,它選擇可重現的統計方法而不是追求複雜模型:每筆警示都有 reason code、數值證據與當下使用的歷史基準;每次評估都保留 false positive、false negative 與三組固定門檻的比較。
要解決的問題,不只是「找出大額交易」
固定金額門檻很容易理解,但它忽略了帳戶差異。對每天交易數十萬美元的帳戶,9,000 美元也許很普通;對長期只交易幾百美元的帳戶,同樣金額可能是重大變化。真正有用的比較對象,通常是「這個帳戶在這個時間點之前的自己」。
個體基準不同
不能用單一全域平均值解釋所有帳戶;需要依帳戶建立歷史金額、速度、裝置與地理基準。
未來資訊洩漏
若特徵偷看到事件之後的交易或測試標籤,離線成績會很好看,但真實上線時無法重現。
誤報沒有脈絡
旅行、換手機、薪資日與資金調度都可能看似異常。系統要保留證據,不能直接替人下結論。
所以專案採用 chronological split:前 60% 時間資料作為訓練基準,中間 20% 驗證,最後 20% 測試。每一列特徵只使用當下或更早事件,ground-truth label 絕不進入規則與特徵。
解法:把偏離行為拆成四層透明證據
資料品質與時間點
驗證欄位、UTC 時間、重複交易與缺值;保留 outlier,不在清理階段把真正要檢查的異常刪掉。
帳戶歷史特徵
計算過去平均金額、24 小時交易量、每小時筆數、新裝置比例、休眠天數與對手方多樣性。
多方法統計共識
同時比較 Z-score、Modified Z-score、MAD、IQR、歷史 p99 與 rolling statistics;金額規則至少要兩種方法同意。
原因與可查證數值
輸出 reason code、severity、交易值、歷史中位數與方法分數,讓分析師能指出觸發原因。
它可以辨識哪些模式?
規則不只看金額。偵測目錄包括交易速度突然增加、快速進出、休眠帳戶重新啟動、地理登入變化、新裝置提款、24 小時高對手方多樣性、行為突然改變、門檻下重複提款與不尋常時段。每一種模式都有明確 reason code,避免所有情況都被模糊地稱為「高風險」。
一筆警示是怎麼產生的?
假設帳戶過去的交易中位數約為 240 美元,這次卻提款 9,800 美元,而且剛好使用第一次出現的裝置。系統不是因為「9,800」這個數字本身就宣判可疑,而是分開建立兩條證據:
- 金額相對帳戶歷史分布大幅偏離,Modified Z-score 達 9.4,並有其他統計方法同意。
- 提款裝置過去從未出現在這個帳戶的事件歷史中。
- 兩個獨立理由合併成一筆較高優先級的人工複核訊號。
{
"transaction_id": "TX-0001824",
"account_id": "ACC-00001",
"anomaly_score": 0.87,
"severity": "high",
"reason_codes": [
"AMOUNT_STATISTICAL_OUTLIER",
"NEW_DEVICE_WITHDRAWAL"
],
"explanation": {
"transaction_amount": 9800,
"account_median_amount": 240,
"modified_zscore": 9.4
}
}0.87 是規則強度的可稽核排序,不是「87% 犯罪機率」。它只表示這筆交易值得比其他交易更早被查看。
如何實際執行這個專案
環境建立與套件安裝方式保留在專案 README;這裡只列真正影響分析流程的五個 CLI 階段,讓讀者把注意力放在資料如何一路變成可解釋警示。
python -m anomaly_detector validate-data
python -m anomaly_detector build-features
python -m anomaly_detector detect
python -m anomaly_detector evaluate
python -m anomaly_detector generate-report這五步各自在做什麼?
- validate-data:確認必要欄位、時間格式、重複 ID、缺值與金額範圍,並寫出 validation report。
- build-features:依帳戶與時間排序,建立只使用當下及過去事件的 rolling / expanding features。
- detect:套用 Conservative、Balanced、Sensitive 三組固定門檻,產生逐筆警示與解釋。
- evaluate:在未參與門檻調整的 chronological test split 上計算 precision、recall、F1 與混淆矩陣。
- generate-report:一次輸出 JSON、CSV、誤報/漏報案例、Markdown 分析與七張圖。
若要接到另一個專案匯出的交易 CSV,可使用 --input path/to/transactions.csv 指定來源,不需要修改偵測核心。
本次實際執行的操作截圖
以下不是示意圖,也不是重新編造的數值;畫面內容來自本次在專案環境中實際執行 CLI 後擷取的 stdout。每張圖對應一個可重現階段,方便把指令、輸出與後面的分析結果接起來。
怎麼讀實測結果,而不是只看最高 F1
三組 operating profile 代表三種營運選擇。Conservative 讓案件較少,但會漏掉更多已植入情境;Sensitive 盡量涵蓋異常,但分析師需要處理更多誤報;Balanced 是示範中的預設人工隊列。
| Profile | Precision | Recall | F1 | 營運意義 |
|---|---|---|---|---|
| Conservative | 0.882 | 0.488 | 0.628 | 案件少,但遺漏較多 |
| Balanced | 0.920 | 0.893 | 0.906 | 預設人工複核隊列 |
| Sensitive | 0.888 | 0.988 | 0.935 | 涵蓋廣,審查負荷增加 |
這些數字證明程式在已知的合成情境上按設計運作,不能直接當成真實環境的詐欺偵測成績。更重要的是,報告會把 false positive 與 false negative 個案完整留下:例如旅行、換裝置與資金調度可能造成誤報;早期 burst 事件則可能因 rolling count 尚未跨過門檻而漏報。
程式依本次輸出資料產生的分析圖表
這一節與上面的操作截圖不同:以下七張圖是專案的 reporting 程式依本次實際執行所產生的資料繪製,用來解讀門檻取捨、錯誤類型與帳戶行為,不是手動另外畫出的示意圖。
限制、誤報,以及人工判斷留在哪裡
這套方法刻意保持透明,但透明不代表沒有盲點。新帳戶的歷史很短,規則會比較保守;帳戶層級特徵看不到多跳錢包網路;國家與裝置變化缺乏登入驗證脈絡;固定美元門檻也不一定符合不同組織或法域。
- 合成標籤只能驗證已設計的情境,無法代表真實對手的適應行為。
- anomaly score 沒有校準成犯罪或詐欺機率。
- 同一個 reason code 可能同時有風險與合理商業解釋。
- 門檻調整必須同時看漏報、誤報、案件量與分析師 SLA。
警示只啟動人工複核。分析師仍要查資料品質、客戶背景、錢包來源、預期商業活動與適用政策,才能決定是否升級案件。