器學(xué)習(xí)反欺詐實(shí)戰(zhàn):基于IEEE-CIS數(shù)據(jù)集的LightGBM二分類與特征工程全流程)
簡(jiǎn)介針對(duì)IEEE-CIS欺詐檢測(cè)競(jìng)賽的探索性數(shù)據(jù)分析EDA與特征工程資源面向參加Kaggle比賽或研究金融風(fēng)控反欺詐的算法工程師適合具備一定機(jī)器學(xué)習(xí)基礎(chǔ)、希望快速上手金融風(fēng)控場(chǎng)景的中高級(jí)學(xué)習(xí)者。任務(wù)本質(zhì)是二分類判斷用戶行為是否屬于點(diǎn)擊欺詐資源圍繞此目標(biāo)給出完整分析流程。主文件Jupyter筆記包含數(shù)據(jù)分布、缺失情況、相關(guān)性等深度洞察配套數(shù)據(jù)清洗、可視化、特征工程三個(gè)Python腳本可高效處理大規(guī)模交易與身份信息表格。壓縮包共6個(gè)文件涵蓋Jupyter筆記、Python腳本與說明文檔大小僅1.02MB。已有599人學(xué)習(xí)。筆記可引導(dǎo)讀者從原始表格到洞察結(jié)論掌握單變量/雙變量分析與聚合特征構(gòu)造思路Python腳本則提供缺失值填補(bǔ)、類型轉(zhuǎn)換、圖表報(bào)告等可復(fù)用函數(shù)便于遷移至相似反欺詐項(xiàng)目也可直接作為數(shù)據(jù)科學(xué)課程教學(xué)演示。整體代碼結(jié)構(gòu)清晰能有效縮短EDA流程、提升建模效率。1. IEEE-CIS-Fraud-Detection 是什么一份能直接復(fù)現(xiàn)的欺詐檢測(cè)全流程IEEE-CIS-Fraud-Detection 很適合用來練手表格類機(jī)器學(xué)習(xí)一份 Jupyter Notebook 把約 59 萬條交易、394 個(gè)脫敏特征、約 3.5% 的欺詐正樣本做成可訓(xùn)練、可驗(yàn)證、可擴(kuò)展的二分類方案核心任務(wù)是預(yù)測(cè)每筆交易的 isFraud 是否為 1評(píng)估指標(biāo)用 ROC AUC。它適合剛?cè)腴T風(fēng)控和異常檢測(cè)的開發(fā)者想把特征工程、LightGBM 交叉驗(yàn)證整條鏈路跑通也適合已經(jīng)在用業(yè)務(wù)規(guī)則做反欺詐的工程師想看看機(jī)器學(xué)習(xí)能不能替代寫死的閾值。整個(gè)流程不依賴 GPU16GB 內(nèi)存的機(jī)器就能跑完Notebook 里每個(gè) cell 都保留了中間輸出方便逐行對(duì)照。它不只是一個(gè)模型文件而是一套從原始 CSV 到提交結(jié)果的完整流程換數(shù)據(jù)集時(shí)改改列名就能復(fù)用。2. 先摸清工程結(jié)構(gòu)Transaction 與 Identity 兩張表、394 個(gè)特征和 isFraud 目標(biāo)拿到這份資源的第一步不是急著跑代碼而是先搞清楚數(shù)據(jù)從哪來、表怎么關(guān)聯(lián)、每個(gè)字段屬于哪一族。這一步?jīng)]做透后面寫特征工程時(shí)很容易把兩套表混在一起處理等模型訓(xùn)完才發(fā)現(xiàn) join 錯(cuò)了行數(shù)返工成本很高。2.1 目錄結(jié)構(gòu)Notebook、數(shù)據(jù)輸入、訓(xùn)練輸出三方分離整套工程在目錄上按「原始數(shù)據(jù) / Notebook / 輸出」三個(gè)區(qū)域分開實(shí)際下載后大致是這個(gè)樣子ieee-cis-fraud-detection/ ├── input/ # 原始 CSV只讀不寫 │ ├── train_transaction.csv # 主表約 59 萬行394 列 │ ├── train_identity.csv # 身份設(shè)備表約 14 萬行41 列 │ └── test_transaction.csv # 測(cè)試集無 isFraud 標(biāo)簽 ├── notebooks/ │ ├── 01_eda.ipynb # 數(shù)據(jù)概覽、缺失率、目標(biāo)分布 │ ├── 02_feature_engineering.ipynb # 特征工程全流程 │ └── 03_train_lgb.ipynb # 五折訓(xùn)練 OOF 評(píng)估 提交 ├── output/ │ └── submission.csv └── README.md這里有個(gè)值得學(xué)習(xí)的設(shè)計(jì)input 目錄只放原始 CSV路徑不硬編碼在 Notebook 里output 統(tǒng)一收預(yù)測(cè)結(jié)果。這樣換機(jī)器、換環(huán)境時(shí)不需要改動(dòng)任何 cell訓(xùn)練產(chǎn)物也永遠(yuǎn)不會(huì)污染數(shù)據(jù)目錄。三個(gè) Notebook 按 01、02、03 編號(hào)本身就是在表達(dá)「先看數(shù)、再提特征、最后建模」的先后依賴關(guān)系你照著編號(hào)順序執(zhí)行就能復(fù)現(xiàn)完整流程。2.2 兩張?jiān)幢淼年P(guān)聯(lián)關(guān)系按 TransactionID 做擴(kuò)展Identity 只覆蓋四分之一主表是 train_transaction.csv每一行是一筆交易包含交易本身的時(shí)間、金額、卡片、地址信息。train_identity.csv 則記錄設(shè)備指紋和身份相關(guān)字段需要通過 TransactionID 關(guān)聯(lián)回主表。注意 identity 表只覆蓋了約四分之一的主表記錄——不是每筆交易都有設(shè)備信息。這個(gè)覆蓋率差異直接決定了特征工程的處理方式主表自身的字段可以全量參與特征構(gòu)建而 identity 表 join 不上來的行只能按缺失值處理。有人會(huì)先把兩張表左連接成一張大寬表再統(tǒng)一處理也有人分開構(gòu)造特征矩陣后再合并我一般傾向后者因?yàn)?identity 字段缺失率本身就是一個(gè)很有價(jià)值的信息維度分開處理更清晰。2.3 特征族譜394 列到底在描述什么394 個(gè)特征看著嚇人拆成族譜后其實(shí)就八類。競(jìng)賽方對(duì)原始字段做了脫敏和匿名化但字段命名規(guī)律仍然保留了分組信息特征族字段范圍代表的業(yè)務(wù)含義交易基礎(chǔ)TransactionDT、TransactionAmt、ProductCD時(shí)間偏移秒、交易金額、產(chǎn)品編碼卡片信息card1 ~ card6發(fā)卡組織、卡種、卡號(hào)區(qū)間等脫敏字段地址與距離addr1、addr2、dist1、dist2交易地址編碼、收貨距離計(jì)數(shù)族C1 ~ C14與賬戶/卡片相關(guān)的計(jì)數(shù)型聚合時(shí)間差族D1 ~ D15與上次同類行為的時(shí)間間隔匹配族M1 ~ M9各類身份校驗(yàn)是否匹配匿名數(shù)值族V1 ~ V339脫敏后的連續(xù)變量缺失率普遍偏高身份設(shè)備族id_01 ~ id_38、DeviceType、DeviceInfo設(shè)備指紋、瀏覽器類型、操作系統(tǒng)V1 到 V339 是篇幅最大的一個(gè)族也是缺失率最嚴(yán)重的地方。這類字段通常是原始特征經(jīng)過匿名變換后的產(chǎn)物語(yǔ)義已經(jīng)不可解讀只能當(dāng)作純數(shù)值特征喂給模型缺失率過高的列直接丟棄。2.4 目標(biāo)變量與評(píng)估口徑為什么是 ROC AUC 而不是準(zhǔn)確率isFraud 的正樣本比例約 3.5%屬于典型的類別不平衡場(chǎng)景。如果拿準(zhǔn)確率當(dāng)指標(biāo)模型全預(yù)測(cè) 0 也能拿到 96.5% 的準(zhǔn)確率但這個(gè)模型毫無價(jià)值。ROC AUC 評(píng)估的是排序能力隨機(jī)抽一個(gè)欺詐樣本和一個(gè)正常樣本模型給欺詐樣本打出更高分?jǐn)?shù)的概率。這個(gè)口徑和反欺詐業(yè)務(wù)是對(duì)齊的——生產(chǎn)環(huán)境里你不會(huì)只卡一個(gè)閾值而是按分?jǐn)?shù)從高到低排序依次審核。AUC 高意味著同樣的審核人力下能撈到更多欺詐交易。所以 Notebook 里第一個(gè) cell 通常是打印正負(fù)樣本比例和 TransactionDT 的時(shí)間跨度先把基本盤看清。3. 特征工程拆解內(nèi)存壓縮、缺失填充、組合特征與聚合特征的四個(gè)關(guān)鍵動(dòng)作這個(gè)項(xiàng)目的分?jǐn)?shù)高低八成取決于特征工程而不是模型本身。LightGBM 是出了名的給好特征就出好結(jié)果反過來也一樣——原始 394 列直接丟進(jìn)模型AUC 通常只能做到 0.90 上下把特征工程做完整同樣的模型能上到 0.95 左右。差距全在這幾個(gè)動(dòng)作里。3.1 內(nèi)存優(yōu)化float64 到 float32先讓 394 列裝得進(jìn)內(nèi)存59 萬行乘以 394 列pandas 默認(rèn)按 float64 讀取時(shí)單表內(nèi)存占用輕松超過 2GB再加上身份表、測(cè)試集和中間變量16GB 內(nèi)存很快見底。這個(gè)項(xiàng)目場(chǎng)景下的標(biāo)準(zhǔn)做法是寫一個(gè)降精度函數(shù)import numpy as np import pandas as pd def reduce_mem_usage(df: pd.DataFrame) - pd.DataFrame: 把數(shù)值列從 float64/int64 降到夠用的精度內(nèi)存直接減半 for col in df.columns: col_type df[col].dtype if col_type object: continue # 字符串列后面單獨(dú)做編碼 c_min df[col].min() c_max df[col].max() if str(col_type)[:3] int: if c_min np.iinfo(np.int8).min and c_max np.iinfo(np.int8).max: df[col] df[col].astype(np.int8) elif c_min np.iinfo(np.int16).min and c_max np.iinfo(np.int16).max: df[col] df[col].astype(np.int16) else: df[col] df[col].astype(np.int32) else: df[col] df[col].astype(np.float32) # 浮點(diǎn)一律壓到 float32 return df train reduce_mem_usage(train)這個(gè)函數(shù)的邏輯是對(duì)整數(shù)列按取值范圍選最小的整型int8/int16/int32對(duì)浮點(diǎn)列一律壓到 float32。LightGBM 訓(xùn)練對(duì)數(shù)值精度不敏感float32 和 float64 的預(yù)測(cè)差異基本在 AUC 小數(shù)點(diǎn)后第四位但內(nèi)存直接省一半。有個(gè)細(xì)節(jié)要注意邊界判斷用的是而不是如果某列的最小值恰好等于 -128 會(huì)被漏判自己復(fù)用時(shí)改成更穩(wěn)妥。3.2 缺失值處理與 label encoding-1 占位是這類項(xiàng)目的默認(rèn)動(dòng)作V 列大量缺失、identity 表覆蓋率只有四分之一意味著缺失值處理是整個(gè)特征工程里最繞不開的一步。常見的做法是先統(tǒng)計(jì)每列缺失率把缺失超過 80% 的列直接丟掉剩余列在訓(xùn)練里交給 LightGBM 的原生缺失值支持處理類別列則先轉(zhuǎn) codes用 -1 統(tǒng)一表示缺失/未知# 統(tǒng)計(jì)缺失率并丟棄高缺失列 missing_rate train.isna().mean() drop_cols missing_rate[missing_rate 0.8].index.tolist() train train.drop(columnsdrop_cols) # 類別列編碼astype(category) 后缺失值自動(dòng)映射為 -1 cat_cols [ProductCD, card1, card2, card3, card4, card5, card6, addr1, addr2, P_emaildomain, R_emaildomain] for col in cat_cols: train[col] train[col].astype(category).cat.codescat.codes會(huì)把每個(gè)類別編號(hào)成從 0 開始的整數(shù)缺失值自動(dòng)編為 -1正好當(dāng)作未知這一類別。要特別注意的是codes 編號(hào)只表示類別序號(hào)不代表大小關(guān)系——card5 的 7 并不比 3 大。所以這類特征喂給 LightGBM 時(shí)必須通過categorical_feature參數(shù)聲明為類別特征否則模型會(huì)把編號(hào)當(dāng)成有序數(shù)值來切分效果會(huì)變差。3.3 組合特征card1_card2 與 addr 的高基數(shù)黑匣子這個(gè)項(xiàng)目里最有名的一個(gè)技巧是組合特征。card1 到 card6 各自脫敏后仍然保留了一定的卡片辨識(shí)度把 card1 和 card2 拼成一個(gè)新類別等于構(gòu)造出這張具體卡片的近似標(biāo)識(shí)。模型就能學(xué)到這張卡過去是否有欺詐歷史這種強(qiáng)規(guī)律train[card1_card2] (train[card1].astype(str) _ train[card2].astype(str)) train[card1_card2] train[card1_card2].astype(category).cat.codes這類組合特征基數(shù)很高很多組合在訓(xùn)練集里只出現(xiàn)一兩次。我的習(xí)慣是構(gòu)造完之后統(tǒng)計(jì)一下基數(shù)如果某個(gè)組合特征的唯一值數(shù)量接近樣本量就說明它已經(jīng)開始背樣本了。此時(shí)要么做頻次過濾出現(xiàn)次數(shù)小于閾值就歸為 -1要么改用組合后的 count/mean 聚合特征保留信息的同時(shí)把基數(shù)降下來。這條經(jīng)驗(yàn)在后面避坑章節(jié)還會(huì)再展開。3.4 時(shí)間與聚合特征從 TransactionDT 拆小時(shí)按 card1 算金額統(tǒng)計(jì)TransactionDT 是以秒為單位的相對(duì)時(shí)間戳直接當(dāng)數(shù)值特征用只能讓模型學(xué)會(huì)第幾天信息密度太低。通過取模運(yùn)算可以還原出小時(shí)和星期規(guī)律——欺詐交易在一天內(nèi)的分布并不均勻這些周期性信號(hào)模型自己很難從原始秒數(shù)里學(xué)到train[hour] train[TransactionDT] // 3600 % 24 # 一天 24 小時(shí) train[weekday] train[TransactionDT] // 86400 % 7 # 一周 7 天聚合特征方面最常用的是按 card1 分組統(tǒng)計(jì)金額的均值、標(biāo)準(zhǔn)差和最近一次金額train[amt_mean_card1] train.groupby(card1)[TransactionAmt].transform(mean) train[amt_std_card1] train.groupby(card1)[TransactionAmt].transform(std)transform會(huì)把聚合結(jié)果廣播回每一行不需要再 merge也不會(huì)改變行數(shù)。這幾個(gè)特征表達(dá)的是這張卡的歷史平均金額和金額波動(dòng)程度模型拿到它們之后就能識(shí)別出金額明顯偏離卡片歷史習(xí)慣的交易。groupby 聚合結(jié)果是按索引對(duì)齊的如果之前做過行篩選或排序務(wù)必先reset_index再聚合否則特征會(huì)錯(cuò)位——這個(gè)問題每年都有新人踩一次。4. 建模與訓(xùn)練五折交叉驗(yàn)證下 LightGBM 的完整鏈路特征工程做完接下來就是建模。整個(gè)訓(xùn)練鏈路設(shè)計(jì)成五折交叉驗(yàn)證 早停 OOF 匯總?cè)问竭@套結(jié)構(gòu)幾乎是表格類比賽的標(biāo)準(zhǔn)答案換任何二分類數(shù)據(jù)集都能復(fù)用。4.1 數(shù)據(jù)集劃分StratifiedKFold 保持正負(fù)樣本比例3.5% 的正樣本率意味著如果用普通 KFold 隨機(jī)切五折某些折里的欺詐樣本數(shù)可能波動(dòng)很大導(dǎo)致不同折的模型看到的數(shù)據(jù)分布不一致。StratifiedKFold 能保證每一折里 isFraud 的比例都和全量基本一致from sklearn.model_selection import StratifiedKFold skf StratifiedKFold(n_splits5, shuffleTrue, random_state42)n_splits5是權(quán)衡后的選擇折數(shù)太少驗(yàn)證集波動(dòng)大折數(shù)太多則每折訓(xùn)練數(shù)據(jù)變少。shuffleTrue先打散再切分避免原始數(shù)據(jù)按時(shí)間排序后切出偏枯的折random_state42固定下來保證任何人復(fù)現(xiàn)時(shí)拿到完全一樣的劃分。這一點(diǎn)很重要——如果不固定隨機(jī)種子每次跑出來的 AUC 都不一樣你就永遠(yuǎn)說不清分?jǐn)?shù)變化到底是改特征導(dǎo)致的還是隨機(jī)波動(dòng)導(dǎo)致的。4.2 LightGBM 參數(shù)與早停小學(xué)習(xí)率配大輪數(shù)模型主體用 LightGBM核心參數(shù)組合是小學(xué)習(xí)率 大樹數(shù) 早停讓模型在低風(fēng)險(xiǎn)下把容量吃滿import lightgbm as lgb params { objective: binary, # 二分類 metric: auc, # 與競(jìng)賽評(píng)估口徑一致 learning_rate: 0.01, # 小步長(zhǎng)每輪只走一點(diǎn) num_leaves: 64, # 單棵樹葉子數(shù)控制復(fù)雜度 feature_fraction: 0.8, # 每棵樹隨機(jī)用 80% 的特征 bagging_fraction: 0.8, # 每棵樹隨機(jī)用 80% 的行 bagging_freq: 1, # 每輪都做行采樣 min_child_samples: 20, # 葉子最少樣本數(shù)防過擬合 verbosity: -1, # 別刷屏 } oof pd.Series(0, indextrain.index) for fold, (tr_idx, va_idx) in enumerate(skf.split(train, y)): tr lgb.Dataset(train.iloc[tr_idx], y.iloc[tr_idx], categorical_featurecat_cols) va lgb.Dataset(train.iloc[va_idx], y.iloc[va_idx], categorical_featurecat_cols) model lgb.train(params, tr, num_boost_round10000, valid_sets[va], callbacks[lgb.early_stopping(200)])learning_rate0.01配num_boost_round10000的意思是每棵樹只學(xué)一點(diǎn)點(diǎn)靠輪數(shù)把精度堆上去這樣比大學(xué)習(xí)率少樹更穩(wěn)定尤其適合高基數(shù)特征多的場(chǎng)景。early_stopping(200)指定驗(yàn)證集連續(xù) 200 輪不提升就停并把當(dāng)前最好輪次記在best_iteration上。feature_fraction和bagging_fraction是列采樣與行采樣兩個(gè)都開能顯著降低特征之間的共線性影響——本項(xiàng)目里 V 列之間相關(guān)性很高這個(gè)設(shè)置價(jià)值尤其大。4.3 OOF 匯總與特征重要性不靠單折分?jǐn)?shù)下結(jié)論五折訓(xùn)完把每一折驗(yàn)證集的預(yù)測(cè)拼回原位置得到 OOFout-of-fold預(yù)測(cè)。它等價(jià)于每個(gè)樣本都被一個(gè)沒見過它的模型預(yù)測(cè)了一次用這個(gè)輸出去算 AUC才是真實(shí)泛化性能的估計(jì)from sklearn.metrics import roc_auc_score oof_score roc_auc_score(y, oof) print(fOOF AUC {oof_score:.5f})要注意 predict 時(shí)必須指定num_iterationmodel.best_iteration否則會(huì)用完整 10000 棵樹預(yù)測(cè)而早停已經(jīng)證明后面的樹在驗(yàn)證集上是負(fù)貢獻(xiàn)。特征重要性則用 gain 而非 split 來排序imp pd.DataFrame({ feature: train.columns, gain: model.feature_importance(gain), }).sort_values(gain, ascendingFalse) imp.head(20)gain 表示該特征在分裂中帶來的信息增益總和比 split分裂次數(shù)更能反映實(shí)際貢獻(xiàn)。通常跑完第一次就能看到 card1_card2、amt_mean_card1 這類特征排在最前面這時(shí)候你就能確信特征工程的方向沒錯(cuò)。5. 常見坑與排查內(nèi)存爆炸、驗(yàn)證集虛高和背題特征的翻車記錄這套流程踩過的坑不少我挑四個(gè)最有代表性的寫下來每條都是「現(xiàn)象 → 原因 → 解決」三段式你照著排查能省不少時(shí)間。5.1 讀取后內(nèi)存直接爆掉現(xiàn)象read_csv 加載 train_transaction.csv 后進(jìn)程被殺或 Jupyter 內(nèi)核崩潰查看任務(wù)管理器發(fā)現(xiàn)內(nèi)存占用接近 20GB。 原因pandas 默認(rèn)把數(shù)值列讀成 float64 和 int64394 列堆下來非常占內(nèi)存中間如果再?gòu)?fù)制一次 DataFrame內(nèi)存直接翻倍。 解決讀取時(shí)就指定 dtype 或用前面寫的reduce_mem_usage函數(shù)第一時(shí)間壓縮。另一個(gè)辦法是先pd.read_csv(..., nrows1000)探測(cè)每列類型再帶著精確的 dtype 字典全量讀取兩種方式都能把峰值內(nèi)存壓低到 4GB 以內(nèi)。5.2 隨機(jī)劃分驗(yàn)證 AUC 虛高時(shí)序劃分分?jǐn)?shù)掉一截現(xiàn)象StratifiedKFold 隨機(jī)劃分時(shí) OOF AUC 穩(wěn)定在 0.95 以上換成按 TransactionDT 排序后切驗(yàn)證集AUC 立刻掉到 0.92 左右。 原因欺詐行為在時(shí)間上存在聚集性隨機(jī)打散等于把未來的欺詐模式泄漏進(jìn)了訓(xùn)練集。模型學(xué)到的是時(shí)間段規(guī)律而不是跨時(shí)間的泛化規(guī)律這在業(yè)務(wù)上不可用——線上永遠(yuǎn)只有過去的數(shù)據(jù)。 解決訓(xùn)練之前先做一致性檢查跑兩版實(shí)驗(yàn)一版隨機(jī)劃分、一版按時(shí)間順序劃分兩個(gè)分?jǐn)?shù)都記錄下來。如果時(shí)間劃分分?jǐn)?shù)明顯更低說明特征里時(shí)間相關(guān)信號(hào)過重優(yōu)先剔除與 TransactionDT 強(qiáng)相關(guān)的列或者干脆以時(shí)間劃分的分?jǐn)?shù)作為主指標(biāo)。5.3 加上 card1_card2 后訓(xùn)練 AUC 逼近 1驗(yàn)證集卻波動(dòng)很大現(xiàn)象引入組合特征后訓(xùn)練集 AUC 沖到 0.99但五折驗(yàn)證分?jǐn)?shù)互相能差 0.02穩(wěn)定不下來。 原因組合特征基數(shù)太高很多組合在訓(xùn)練集里只出現(xiàn)幾次甚至一次模型直接把這些樣本背下來了遇到?jīng)]見過的組合就亂猜。 解決對(duì)組合特征做頻次過濾把出現(xiàn)次數(shù)低于 10 的組合統(tǒng)一映射成 -1或者退一步用組合后的 count 特征每個(gè)組合出現(xiàn)的次數(shù)和金額聚合替代原始組合編碼信息還在基數(shù)可控。記住一個(gè)判斷標(biāo)準(zhǔn)特征唯一值數(shù)量超過樣本量的 5%基本就是在背題。5.4 本地 AUC 正常提交分?jǐn)?shù)對(duì)不上現(xiàn)象OOF AUC 0.95提交后分?jǐn)?shù)遠(yuǎn)低于本地甚至接近隨機(jī)。 原因測(cè)試集在特征工程階段被 shuffle 或篩選過預(yù)測(cè)階段輸出 submission.csv 時(shí)行序沒有對(duì)齊原始索引標(biāo)簽和特征錯(cuò)位。 解決從訓(xùn)練階段就把流程固化下來——預(yù)測(cè)前test.reset_index(dropTrue)把索引重置干凈后再做特征工程保存提交文件時(shí)按原始 TransactionID 排序并校驗(yàn)行數(shù)。這個(gè)坑屬于典型的過程正確但結(jié)果全錯(cuò)光看本地分?jǐn)?shù)根本發(fā)現(xiàn)不了。6. 二次開發(fā)思路把這套 Notebook 改造成自己的風(fēng)控基線復(fù)現(xiàn)只是第一步這套資源真正的價(jià)值在于可以遷移到自己的業(yè)務(wù)數(shù)據(jù)上。我的改造路徑分三步走。第一步是換數(shù)據(jù)源時(shí)的列名映射。拿自己系統(tǒng)的交易表過來先寫一個(gè)字段映射字典把業(yè)務(wù)字段對(duì)齊到這套工程的主表字段上field_map { pay_time: TransactionDT, pay_amount: TransactionAmt, user_id: card1, merchant_id: card2, } train train.rename(columnsfield_map)映射完先跑 01_eda 看分布確認(rèn)金額、時(shí)間兩列的語(yǔ)義一致再往下走。第二步是把特征工程代碼從 Notebook 里抽成獨(dú)立模塊訓(xùn)練和預(yù)測(cè)共用同一份feature_engineering.py避免兩個(gè) Notebook 各寫一套特征邏輯。第三步才是換模型——補(bǔ)一個(gè) XGBoost 或 CatBoost兩個(gè)模型預(yù)測(cè)分?jǐn)?shù)做加權(quán)平均用 OOF AUC 決定權(quán)重一般從 0.6/0.4 起步試。部署上線時(shí)我會(huì)把訓(xùn)練好的模型用booster.save_model(lgb.txt)導(dǎo)出成文本文件服務(wù)端加載后在線預(yù)測(cè)前先把原始請(qǐng)求字段塞進(jìn)同一個(gè)特征工程函數(shù)保證訓(xùn)練和線上特征完全一致。這里最容易翻車的是線上字段缺失——請(qǐng)求里少一個(gè)字段函數(shù)要按缺失值邏輯兜底而不是拋異常返回 500。從那以后我每次拿到一個(gè)新數(shù)據(jù)集都會(huì)強(qiáng)制走一遍「列名映射 → 特征工程函數(shù)化 → 隨機(jī)/時(shí)序雙驗(yàn)證」這三步基本杜絕了訓(xùn)練和線上不一致的翻車。這個(gè)項(xiàng)目作為起點(diǎn)已經(jīng)足夠完整剩下的就是你自己的數(shù)據(jù)了希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取