写点什么

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

  • 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:003568
用户头像
刘燕 InfoQ高级技术编辑

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

关注

评论

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

【2023云栖】郭瑞杰:阿里云搜索产品智能化升级

阿里云大数据AI技术

搜索引擎

IP代理如何助力你的营销业务?如何选择?

Geek_ccdd7f

持续集成指南:GitHubAction 自动构建+部署AspNetCore项目

EquatorCoco

GitHub 持续集成 集成测试

亲身体验云原生顶会北美 KubeCon,5个要点和4个 Fun Facts

小猿姐

Kubernetes 云原生 cncf KubeCON

有限元分析初学者需要关注哪些问题?

思茂信息

仿真软件 仿真技术 有限元分析 有限元仿真 有限元技术

MySQL索引入门

互联网工科生

MySQL 数据库

分布式应用服务的拆分

快乐非自愿限量之名

分布式 开发应用 应用开发

五个提升SQL语句性能的小窍门,进一步提升查询性能

高端章鱼哥

数据库 sql

大数据的技术运用:探索未来的无限可能性

EquatorCoco

大数据 技术应用 城市智能化 医疗健康

详解数据库SQL中的三个语句:DROP、TRUNCATE 、DELETE

华为云开发者联盟

数据库 后端 华为云 华为云GaussDB 华为云开发者联盟

软件测试 |人工智能数据生成,ChatGPT引领测试新风潮

测吧(北京)科技有限公司

测试

来听B站音乐UP主从容老师讲解GuitarPro和Earmaster

淋雨

Guitar Pro EarMaster 吉他 声乐 视唱

弹性云主机支持多种规格

天翼云开发者社区

云计算 云主机 云平台

如何挑选挑选到优质的HTTP代理平台呢?

Geek_ccdd7f

人工智能 | 开启智慧学习新时代—与AI对话,高效提升学习效率

测吧(北京)科技有限公司

测试

计算网络之MSTP协议与VRRP协议

不在线第一只蜗牛

计算机网络 计算机知识 协议解析

超过5000+企业使用的ETL平台

RestCloud

ETL

软件测试/测试开发丨人工智能产品质量保障:挑战与创新

测试人

人工智能 软件测试

软件测试 |人工智能引领未来学习趋势——ChatGPT智能学习助手

测吧(北京)科技有限公司

测试

Live Home 3D Pro for mac(3D家居设计软件) 4.8.3永久激活版

mac

3D家居设计软件 苹果mac Windows软件 Live Home 3D Pro

避免defer陷阱:拆解延迟语句,掌握正确使用方法

伤感汤姆布利柏

专家分享——CAE仿真软件学习心得

智造软件

仿真 CAE 仿真软件 CAE软件 altair

MatrixOne完成与欧拉、麒麟信安的兼容互认

MatrixOrigin

分布式数据库 云原生数据库 MatrixOrigin MatrixOne HTAP数据库

数字孪生协同仿真:复杂电机篇

DevOps和数字孪生

数字孪生 电机 仿真建模

你这些网站都不知道,怎么摸好鱼?

秃头小帅oi

前端 低代码 在线 程序选

云电脑运行原理分析

天翼云开发者社区

虚拟化 云平台 云电脑

Xmind for Mac(思维导图软件) 24.01永久激活版

mac

XMind 思维导图软件 苹果mac Windows软件

三策略,六步骤,Jenkins 迁移到极狐GitLab CI 的终极指南

极狐GitLab

ci DevOps gitlab 持续集成 jenkins

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