写点什么

实战贴:如何使用机器学习检测欺诈?

  • 2020-09-25
  • 本文字数:5737 字

    阅读完需:约 19 分钟

实战贴:如何使用机器学习检测欺诈?

本文最初发表于 Towards Data Science 博客,经原作者 Kurtis Pykes 授权,InfoQ 中文站翻译并分享。


机器学习是人工智能的一个子集,它赋予了系统从经验中自动学习和改进的能力,无需进行显式编程。如此说来,我们(人类)已经可以向计算机提供大量的数据集,让计算机学习模式,这样它在面对一个或多个新实例时,能够学习如何作出决定——当我发现这一见解时,我立即知道世界即将发生改变。


报告显示,欺诈行为给全球经济造成了 3.89 万亿英镑的损失,在过去十年里损失上升了 56%。

——Crowe UK


作为欺诈行为的受害者,我萌生了防止这种情况再次发生在我(以及其他任何人)身上的想法,这促使我开始思考一个与我所习惯的完全不同的领域。

欺诈检测问题

在机器学习术语中,诸如欺诈检测之类的问题,可以被归类为分类问题,其目标是预测离散标签 0 或 1,其中,0 通常表示交易是非欺诈性的,1 表示交易似乎是欺诈性的。


因此,这个问题要求从业人员构建足够智能的模型,以便能够在给定各种用户交易数据的情况下,正确地检测出欺诈性和非欺诈性的交易。为了保护用户隐私,这些交易数据通常都经过匿名化处理。


由于完全依赖基于规则的系统并不是最有效的策略,因此,机器学习已成为许多金融机构用来解决这一类问题的方法。


这个问题(欺诈检测)之所以如此具有挑战性,是因为当我们在现实世界对其进行建模时,发生的大多数交易都是真实的交易,只有很小一部分是欺诈行为。这意味着我们要处理数据不平衡的问题:我写的文章《过采样和欠采样》(Oversampling and Undersampling)就是处理这一类问题的一种方法。然而,对于这篇文章,我们的主要重点将是开始我们的机器学习框架来检测欺诈行为——如果你不熟悉构建自己的框架,那你可能需要在阅读本文之前,先阅读这篇文章《构建机器学习项目》(Structuring Machine Learning Projects)。

数据

这些数据是由IEEE 计算智能协会(IEEE Computational Intelligence Society,IEEE-CIS)的研究人员整理出来的,用于预测欺诈性在线交易概率的任务,以二进制目标isFraud来表示。


注:数据部分是从 Kaggle 竞赛数据部分复制而来。


数据分成两个文件identitytransaction,这两个文件由TransactionID连接。但并非所有交易都有相应的身份信息。

类别特征——交易(Transaction)

  • ProductCD

  • card1-card6

  • addr1addr2

  • P_emaildomain

  • R_emaildomain

  • M1-M9

类别特征——身份信息(Identity)

  • DeviceType

  • DeviceInfo

  • id_12-id_38


TransactionDT特征是给定引用日期时间(不是实际时间戳)开始的时间间隔(timedelta)。


你可以从比赛主持人的这篇文章《数据描述(详情及讨论)》(Data Description (Details and Discussion))中了解更多有关数据的信息。

文件

  • train_{transaction, identity}.csv——训练集

  • test_{transaction, identity}.csv——测试集(你必须预测这些观察值的isFraud值)

  • sample_submission.csv——正确格式的样本提交文件

构建框架

在处理任何机器学习任务时,第一步是建立一个可靠的交叉验证策略。


注:该框架背后的总体思路来自于Abhishek Thakur

——GitHub


当面对不平衡的数据问题时,通常采用的方法是使用StratifiedKFold,它以这样一种方式随机地分割数据,以保持相同的类分布。


我实现了 create folds,作为preprocessing.py的一部分。


import configimport numpy as npimport pandas as pdfrom sklearn.model_selection import StratifiedKFolddef read_all_data():train_transactions = pd.read_csv(config.TRAIN_TRANSACTIONS)train_identity = pd.read_csv(config.TRAIN_IDENTITY)test_transactions = pd.read_csv(config.TEST_TRANSACTIONS)test_identity = pd.read_csv(config.TEST_IDENTITY)return train_transactions, train_identity, test_transactions, test_identitydef merge_data(df1, df2):# merge dataframe on the indexmerged_df = df1.merge(df2, how="left", on="TransactionID")return merged_dfdef create_folds(df):# create a new columndf["kfold"] = -1# shuffle datadf = df.sample(frac=1, random_state=42).reset_index(drop=True)# initialize kfoldskf = StratifiedKFold(n_splits=5, shuffle=False)for fold, (train_idx, val_idx) in enumerate(skf.split(X=df, y=df.isFraud.values)):print(len(train_idx), len(val_idx))df.loc[val_idx, 'kfold'] = folddf.to_csv(config.DATA_DIR + "train_folds.csv", index=False)if __name__ == "__main__":train_transactions, train_identity, test_transactions, test_identity = read_all_data()merged_test = merge_data(test_transactions, test_identity)merged_train = merge_data(train_transactions, train_identity)del train_transactions, train_identity, test_transactions, test_identity# renaming test id columnsfor col in merged_test.columns:if "id" in col:merged_test.rename(columns={col : col.replace("-", "_")}, inplace=True)merged_test.to_csv(config.DATA_DIR + "test_df.csv", index=False)create_folds(merged_train)
复制代码


这段代码合并了来自训练集和测试集的身份信息和交易数据,然后重命名了merded_test数据中的列名,因为 id 列使用的是“-”而不是“_”,这将导致稍后检查以确保测试中的列名完全相同时出现问题。接下来,我们在训练数据中添加一个名为kfold的列名,并根据它所在的 fold 设置索引,然后保存到 CSV 文件中。


你可能已经注意到,我们导入config并将其作为通向各种交易的路径。所有的config都是另一个脚本的变量,这样我们就不必在不同的脚本重复调用这些变量了。


# Directory PathsDATA_DIR = "../input/"MODEL_OUTPUT = "../models"# Training dataTRAINING_DATA = DATA_DIR + "train_folds.csv"TRAIN_TRANSACTIONS = DATA_DIR + "train_transaction.csv"TRAIN_IDENTITY = DATA_DIR + "train_identity.csv"# Test dataTEST_DATA = DATA_DIR + "test_df.csv"TEST_TRANSACTIONS = DATA_DIR + "test_transaction.csv"TEST_IDENTITY = DATA_DIR + "test_identity.csv"# Categorical FeaturesCATEGORICAL_FEATURES = ["ProductCD", "card1", "card2", "card3", "card4","card5", "card6", "addr1", "addr2", "P_emaildomain","R_emaildomain", "M1", "M2", "M3", "M4", "M5","M6", "M7", "M8", "M9", "DeviceType", "DeviceInfo","id_12", "id_13", "id_14", "id_15", "id_16", "id_17","id_18", "id_19", "id_20", "id_21", "id_22", "id_23","id_24", "id_25", "id_26", "id_27", "id_28", "id_29","id_30", "id_31", "id_32", "id_33", "id_34", "id_35","id_36", "id_37", "id_38"]
复制代码


在处理机器学习问题时,以允许快速迭代的方式快速构建管道是非常重要的,因此我们将构建的下一个脚本是model_dispatcher.py,我们将其称为分类器,而train.py是我们的训练模型的脚本。


让我们从model_dispatcher.py开始。


from sklearn import linear_model, ensemblemodels = {"logistic_regression": linear_model.LogisticRegression(verbose=True, max_iter=1000, random_state=10),"random_forest": ensemble.RandomForestClassifier(verbose=True, n_estimators=100, criterion="gini")}
复制代码


在这里,我们简单地导入了一个逻辑回归和随机森林,并创建了一个字典,这样我们就可以通过运行逻辑回归模型models["logistic_regression"]来将算法调用到我们的训练脚本中。


训练脚本如下所示:


import osimport configimport model_dispatcherimport joblibimport argparseimport pandas as pdfrom sklearn import preprocessingfrom sklearn import metricsdef pipe(fold:int, model:str):df = pd.read_csv(config.TRAINING_DATA)df_test = pd.read_csv(config.TEST_DATA)X_train = df[df["kfold"] != fold].reset_index(drop=True)X_valid = df[df["kfold"] == fold].reset_index(drop=True)y_train = X_train.isFraud.valuesy_valid = X_valid.isFraud.valuesX_train = X_train.drop(["isFraud", "kfold"], axis=1)X_valid = X_valid.drop(["isFraud", "kfold"], axis=1)X_valid = X_valid[X_train.columns]label_encoders = {}for c in config.CATEGORICAL_FEATURES:lbl = preprocessing.LabelEncoder()X_train.loc[:, c] = X_train.loc[:, c].astype(str).fillna("NONE")X_valid.loc[:, c] = X_valid.loc[:, c].astype(str).fillna("NONE")df_test.loc[:, c] = df_test.loc[:, c].astype(str).fillna("NONE")lbl.fit(X_train[c].values.tolist() +X_valid[c].values.tolist() +df_test[c].values.tolist())X_train.loc[:, c] = lbl.transform(X_train[c].values.tolist())X_valid.loc[:, c] = lbl.transform(X_valid[c].values.tolist())label_encoders[c] = lbl# data is ready to trainclf = model_dispatcher.models[model]clf.fit(X_train.fillna(0), y_train)preds = clf.predict_proba(X_valid.fillna(0))[:, 1]print(metrics.roc_auc_score(y_valid, preds))joblib.dump(label_encoders, f"{config.MODEL_OUTPUT}/{model}_{fold}_label_encoder.pkl")joblib.dump(clf, f"{config.MODEL_OUTPUT}/{model}_{fold}.pkl")joblib.dump(X_train.columns, f"{config.MODEL_OUTPUT}/{model}_{fold}_columns.pkl")if __name__ == "__main__":parser = argparse.ArgumentParser()parser.add_argument("--fold",type=int)parser.add_argument("--model",type=str)args = parser.parse_args()pipe(fold=args.fold,model=args.model)
复制代码


我希望你能读懂代码,但如果看不明白的话,我来总结一下这段代码所发生的的事情:将训练数据设置为列kfold中的值,并且与我们通过的 fold 相同的值就是测试集。然后,我们对分类变量进行标签编码,并用 0 填充所有缺失值,最后将数据训练到逻辑回归模型上。


我们得到当前的 fold 的预测,并打印出ROC_AUC


注:从目前的情况看,代码本身并不会运行,因此我们必须在运行每个 Fold 时,传递 fold 和 model 的值。


让我们看看逻辑回归模型的输出。


### Logistic Regression# Fold 0ROC_AUC_SCORE: 0.7446056326560758# Fold 1ROC_AUC_SCORE: 0.7476247589462117# Fold 2ROC_AUC_SCORE: 0.7395710927094167# Fold 3ROC_AUC_SCORE: 0.7365641912867861# Fold 4ROC_AUC_SCORE: 0.7115696956435416
复制代码


这些都是相当不错的结果,但让我们使用更强大的随机森林模型,看看是否还可以改善。


### Random Forest# Fold 0ROC_AUC_SCORE: 0.9280242455299264# Fold 1ROC_AUC_SCORE: 0.9281600723876517# Fold 2ROC_AUC_SCORE: 0.9265254015330469# Fold 3ROC_AUC_SCORE: 0.9224746067992484# Fold 4ROC_AUC_SCORE: 0.9196977372298685
复制代码


很明显,随机森林模型产生了更好的结果。让我们在 Kaggle 上进行后期提交,看看我们在排行榜上的位置。这是最重要的部分——要做到这一点,我们必须运行inference.py


import osimport pandas as pdimport numpy as npimport configimport model_dispatcherfrom sklearn import preprocessingfrom sklearn import metricsimport joblibdef predict(test_data_path:str , model_name:str, model_path:str):df = pd.read_csv(test_data_path)test_idx = df["TransactionID"].valuespredictions = Nonefor FOLD in range(5):df = pd.read_csv(test_data_path)encoders = joblib.load(os.path.join(model_path, f"{model_name}_{FOLD}_label_encoder.pkl"))cols = joblib.load(os.path.join(model_path, f"{model_name}_{FOLD}_columns.pkl"))for c in encoders:lbl = encoders[c]df.loc[:, c] = df.loc[:, c].astype(str).fillna("NONE")df.loc[:, c] = lbl.transform(df[c].values.tolist())clf = joblib.load(os.path.join(model_path, f"{model_name}_{FOLD}.pkl"))df = df[cols]preds = clf.predict_proba(df.fillna(0))[:, 1]if FOLD == 0:predictions = predselse:predictions += predspredictions /= 5sub = pd.DataFrame(np.column_stack((test_idx, predictions)), columns=["TransactionID", "isFraud"])return subif __name__ == "__main__":submission = predict(test_data_path=config.TEST_DATA,model_name="random_forest",model_path=f"{config.MODEL_OUTPUT}/")submission.loc[:, "TransactionID"] = submission.loc[:, "TransactionID"].astype(int)submission.to_csv(f"{config.DATA_DIR}/rf_submission.csv", index=False)
复制代码


注:提交给 Kaggle 的过程并不在本文讨论的范畴,因此我将直接在排行榜上列出模型的得分以及它是如何做到的。



考虑到这个分数可以转换成 Kaggle 的私人排行榜(因为它是公共排行榜上的分数),我们在 Kaggle 的私人排行榜上排名为 3875/6351(前 61%)。虽然从 Kaggle 的角度来看,这个得分看起来并不咋样,但在现实世界的场景中,我们可能会根据任务的情况来解决这个分数。


但是,这个项目的目标并非提出最好的模型,而是创建我们自己的 API,我们将在后面的文章中讨论这个问题。


为了构建快速迭代的快速管道,我们拥有的代码是可以的,但是如果我们想部署这个模型的话,就必须做大量的清理工作,这样我们才能遵循软件工程最佳实践

总结

在现实世界中,欺诈检测是一个非常普遍且具有挑战性的问题,提高正确率对于防止在顾客在商店进行真正的交易时信用卡被拒的尴尬非常重要。我们已经构建了一种非常简单的方法,使用分类变量的标签编码,用 0 填充所有缺失值,并使用随机森林,没有任何调整或方法来处理数据的不平衡性,但我们的模型仍然得到了很高的分数。为了改进模型,我们可能要先从随机森林模型中寻找重要的特征,放弃不那么重要的特征,或者我们可以使用其他更为强大的模型,比如 Light Gradient Boosting Machine 和神经网络。


注:在编写这个脚本时,模块并不是最好的,但它的格式允许我进行快速迭代。在以后的工作中,我计划将这个模型作为 API 部署到云服务器上。


作者介绍:


Kurtis Pykes,痴迷于数据科学、人工智能和商业技术应用。


原文链接:


https://towardsdatascience.com/using-machine-learning-to-detect-fraud-f204910389cf


2020-09-25 08:003712
用户头像
刘燕 InfoQ高级技术编辑

发布了 1112 篇内容, 共 542.7 次阅读, 收获喜欢 1978 次。

关注

评论

发布
暂无评论
发现更多内容

欧拉中国服务器操作系统份额达50%,商业技术生态稳健发展

科技热闻

Video Copilot VCReflect for Mac(AE倒影插件) 支持多帧渲染

理理

Delicious Retouch Panel(PS人像磨皮美容插件) v5.0(DR5中文版) 高级版(Win/Mac)

理理

RAG 系统高效检索提升秘籍:如何精准选择 BGE 智源、GTE 阿里与 Jina 等的嵌入与精排模型的完美搭配

汀丶人工智能

rag

哪款工程管理软件最好?2024年十大推荐

爱吃小舅的鱼

工程管理 工程管理软件 工程管理工具

PLM项目管理系统的核心作用:改善产品质量与市场响应

爱吃小舅的鱼

plm项目管理 plm系统 plm系统作用

如何科学地规划和执行项目集进度安排

爱吃小舅的鱼

项目集进度安排

Agents开发实战营总结

铁血杰克

什么是PLM项目管理系统?全面了解其重要性和应用

爱吃小舅的鱼

PLM软件 plm项目管理 plm系统

使用Python根据给定模型计算模型权值

代码忍者

活动组织管理小程序(源码+文档+部署+讲解)

深圳亥时科技

互联网演进跨越半世纪,智能化时代呼唤Net5.5G网络新代际

脑极体

通信

Safari Pedals Everything Bundle for Mac(野生动物园模拟插件合集) v2024.10.14 激活版

理理

fcpx插件: CrumplePop EchoRemover(回声消除器) v2.0.10激活版

理理

淘宝/天猫官方商品/订单订单API接口丨商品上传接口对接步骤

tbapi

天猫店铺订单接口 淘宝开放平台订单接口 淘宝店铺上传接口

负载开关IC:简化电源管理与提升系统稳定性的关键

芯动大师

专业视角解读:高效项目管理的实用技巧

爱吃小舅的鱼

项目管理

CnosDB 实时流式计算:优化时序数据处理与降采样解决方案

CnosDB

时序数据库 tsdb CnosDB #开源

XLN Audio RC-20 Retro Color for Mac(创意效果插件) v1.1.3激活版

理理

Nik Collection by DxO(照片编辑插件套件) v6.12.0中文版+便携版

理理

流程管理工具推荐:8款实用选择

爱吃小舅的鱼

流程管理工具

《Django 5 By Example》阅读笔记:p211-p236

codists

Python django

智慧园区管理系统(源码+文档+部署+讲解)

深圳亥时科技

AE/PR插件:Boris FX Mocha Pro 2024.5(平面跟踪摄像机反求插件) v11.5.0激活版

理理

Essential Presets Bundle for Luminar Neo for mac(Luminar NEO基本预设包)

理理

实战贴:如何使用机器学习检测欺诈?_AI&大模型_Kurtis Pykes_InfoQ精选文章