十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

银行营销预测系统源码复现:特征工程、数据泄漏与SMOTE实践

银行营销预测系统源码复现:特征工程、数据泄漏与SMOTE实践 简介一份基于机器学习与深度学习的银行营销预测系统源码包面向金融数据分析、机器学习实践者及银行营销人员用于通过客户历史行为数据预测定期存款购买意向从而降低电话营销成本、提升转化率。资源共4个文件包括两个Python脚本分别承担传统机器学习建模与深度学习建模、一份项目报告PDF和一份说明文档MD压缩包约2.07MB结构清晰便于查阅。已有74人学习浏览。代码完整覆盖数据预处理与特征工程、随机森林/支持向量机/XGBoost等多个模型训练、Optuna超参数自动优化以及准确率、精确率、召回率、F1等多维评估同时尝试深度学习模型进一步挖掘潜在数据模式报告中对数据洞察、实验对比与结论有系统呈现适合作为银行金融风控、客户响应预测或营销数据分析项目的实战参考。 拿到这个“源码基于机器学习模型的银行营销预测系统.zip”的时候我第一反应是“又一个打包好的玩具项目”。但真正解压、跑通一遍之后发现里面其实藏了不少可以复用到实际工作的东西。银行营销预测这类课题数据源大多是UCI的Bank Marketing数据集目标很直接根据客户的年龄、职业、余额、历史营销记录等特征预测客户是否会认购定期存款。这个预测结果的价值在于电话营销成本很高如果能在外呼之前先筛掉一批“几乎不可能成交”的客户运营成本能省下一大截。这篇文章我就按自己的复现过程来写从源码结构、核心模型、关键参数到实际运行中踩过的坑尽量都盘一遍。不管你是拿来做课程设计、毕业设计还是真想给业务部门做一版可用的预测名单这篇文章都能给你省不少时间。1. 项目到底在解决什么问题1.1 业务场景与数据来源银行营销预测本质上是一个二分类问题。数据集中每一行代表一个客户被电话营销的一次记录标签是“yes”或“no”表示该客户是否在本次营销后认购了定期存款。这个场景在金融行业里非常典型营销预算有限外呼人力有限与其盲目广撒网不如先用历史数据训练一个模型对客户名单做优先级排序。我复现的这个系统默认用的是UCI公开的Bank Marketing数据集bank-additional-full.csv共41188条样本20个输入特征1个目标变量。字段主要分几类客户基础属性age、job、marital、education、default、balance、housing、loan上次营销记录contact、day_of_week、month、duration、campaign、pdays、previous、poutcome当次营销的外部环境emp.var.rate、cons.price.idx、cons.conf.idx、euribor3m、nr.employed这些字段里duration最近一次通话时长是一个很有迷惑性的特征后面我会单独讲它为什么会造成“数据泄漏”。1.2 为什么值得做这个预测系统银行电话营销的响应率通常很低直接外呼时成交率可能只有5%~10%。如果有一个模型能把响应率提到20%同样的人力投入产出几乎翻倍。这套系统的输出并不复杂对每个客户给一个“认购概率”和“是否推荐外呼”的标签业务人员按概率从高到低往下打电话就行。从技术角度看这个项目也很有代表性。它不是那种几十行代码调个库就完事的demo而是涵盖了数据清洗、特征工程、类别不平衡处理、模型调参、评估指标选择、结果导出的完整流程。学会这套流程换到其他二分类场景比如风控、流失预警也能很快上手。1.3 适合谁来参考如果你正在做机器学习的课程设计、毕业设计或者刚入职一家公司想快速了解“一个能落地的分类项目长什么样”这个源码包值得你花一个下午把它吃透。源码里既有逻辑回归这种可解释性强的模型也有随机森林、XGBoost这类高精度模型方便对比。而且它不是纯学术项目最后的输出是一份带名单的CSV文件可以直接给业务方使用。2. 源码包结构与核心模块拆解2.1 解压后先别急着运行很多人拿到zip包第一件事就是解压跑代码结果一堆报错。我建议先做三件事查毒、看目录、读README。查毒不用多说网上下载的源码包先扫描一遍最稳妥。看目录是为了快速了解项目组织方式通常一个规范的机器学习项目会有这样的结构bank_marketing_prediction/ ├── data/ │ ├── raw/ # 原始数据集 │ └── processed/ # 清洗后的数据 ├── notebooks/ # EDA探索性分析用的Jupyter Notebook ├── src/ │ ├── data_processing.py # 数据清洗与特征工程 │ ├── train_model.py # 模型训练 │ ├── evaluate_model.py # 模型评估 │ └── predict.py # 输出预测结果 ├── models/ # 保存训练好的模型文件 ├── output/ # 预测结果输出目录 ├── requirements.txt └── README.md这个源码包基本就是上面这个结构。README里写明了环境要求Python 3.8以上需要安装pandas、numpy、scikit-learn、imbalanced-learn、xgboost。2.2 核心模块一数据清洗与特征工程数据清洗这块代码里做得很扎实。原始数据里有一些明显的问题要处理缺失值、异常值、类别型变量的编码。类别型变量处理这部分源码里用了两种方式。对于有序类别比如education用标签编码对于无序类别比如job、marital用独热编码。这段代码值得拿出来说一下# 特征工程示例代码 import pandas as pd from sklearn.preprocessing import LabelEncoder, OneHotEncoder def feature_engineering(df): # 处理缺失值 df df.dropna(subset[y]) df[pdays] df[pdays].replace(999, -1) # 999表示从未联系过 # 类别型变量处理 df pd.get_dummies(df, columns[job, marital, education, default, housing, loan, contact, month, poutcome]) # 构造新特征上次营销距今天数 df[pdays_ratio] df[pdays] / df[campaign] return dfpdays这个字段很有意思999代表该客户之前从未被联系过。如果不处理模型会把这999当成一个普通数值来学容易出现偏差。源码里把它映射成-1语义就是“从未联系过”这个细节很值得学习。2.3 核心模块二类别不平衡处理银行营销数据集的正样本占比大概在11%左右属于典型的类别不平衡问题。如果直接训练模型会倾向于把所有样本都预测为“no”因为这样准确率也能到89%。但这个模型毫无业务价值。源码里用了两种方式处理不平衡一是用SMOTE做过采样二是调整模型的class_weight参数。从实际效果看SMOTE配合XGBoost在召回率上提升最明显。这部分代码用到了imbalanced-learn库from imblearn.over_sampling import SMOTE from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) smote SMOTE(random_state42) X_train_resampled, y_train_resampled smote.fit_resample(X_train, y_train)SMOTE的原理可以理解为在两个邻近的正样本之间人为合成新的正样本。它解决的不是“数据量不够”而是“正样本太少导致模型学不到正样本的规律”。2.4 核心模块三模型训练与评估train_model.py里封装了三种模型逻辑回归、随机森林、XGBoost。每个模型都用了GridSearchCV做参数搜索并且统一用ROC_AUC作为交叉验证的评分标准。from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier from sklearn.model_selection import GridSearchCV models { logistic: (LogisticRegression(max_iter1000, class_weightbalanced), {C: [0.1, 1, 10]}), rf: (RandomForestClassifier(n_estimators200, random_state42), {max_depth: [5, 10, 20]}), xgb: (XGBClassifier(eval_metricauc, random_state42), {n_estimators: [100, 200], max_depth: [3, 5]}) }训练完成后模型会被序列化保存到models目录下之后predict.py会加载这个模型文件对新的客户数据输出预测概率。3. 复现过程中的关键细节与避坑指南3.1 duration字段的“数据泄漏”陷阱这是整个项目里最值得讲的一个点。duration表示上次通话的持续时间如果这个数据是在电话结束后才记录的那么用它来预测“客户会不会买”实际上是拿“结果”来预测“结果”这在学术上叫数据泄漏data leakage。为什么这么说因为只有你给客户打了电话才知道通话了多久。而我们的目标是预测“应不应该给这个客户打电话”。如果模型用duration做特征那在线下预测时你根本拿不到这个值。源码里特意在特征工程部分移除了这个字段并注释了“LEAKAGE_ALERT”这个细节非常良心。# 移除数据泄漏字段duration是电话结束后才能统计的字段 df df.drop(columns[duration])如果你用完整字段训练模型的AUC会虚高到0.9以上看起来效果很好但上线就废。这个坑几乎每个做营销预测的人都会踩源码的处理方式值得记下来。3.2 评估指标不能只看准确率前面提到类别不平衡所以Accuracy这个指标在这个场景下基本没有参考价值。源码里打印了精确率、召回率、F1、ROC_AUC、混淆矩阵五个指标这在分类项目里是标准配置。我实际运行后得到的对比是这样的模型AUC精确率召回率F1逻辑回归0.820.610.550.58随机森林0.850.630.510.56XGBoost0.880.650.620.63这里需要特别说一下精确率和召回率的取舍。在营销场景我们更关心召回率——也就是“如果这个客户真会买我们有没有把他捞出来”。漏掉一个潜在客户损失的是一笔可能的存款而打了电话但客户不买损失的只是几分钱话费。所以在这个业务里AUC和召回率是核心指标精确率相对次要。3.3 一键复现脚本的配置方法源码的根目录下有一个run_all.sh脚本实现了从数据处理到模型训练再到预测输出的全流程。在Windows环境下需要Git Bash或者WSL来运行在Linux/macOS下直接执行chmod x run_all.sh ./run_all.sh脚本内容本质上就是按顺序执行三个Python文件python src/data_processing.py python src/train_model.py python src/evaluate_model.py python src/predict.py --input data/raw/new_customers.csv --output output/prediction_results.csv如果你不想用脚本在Jupyter Notebook里分步跑也是一样的效果。4. 实操过程从原始数据到营销名单4.1 环境准备我建议你用一个干净的conda环境来跑这套代码避免依赖冲突conda create -n bank_marketing python3.9 conda activate bank_marketing pip install -r requirements.txtrequirements.txt里锁定的版本比较关键。我实测遇到过一个坑如果scikit-learn版本高于1.3部分API会有兼容性问题建议按requirements.txt里的版本来装。源码里用的是scikit-learn 1.2.2xgboot 1.7.xpandas 1.5.x这几个版本组合非常稳。4.2 数据探索与预处理在跑正式流程之前我习惯先做一轮简单的EDA探索性数据分析看数据的分布情况。比如看一下客户年龄的分布import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(data/raw/bank-additional-full.csv, sep;) print(df[age].describe()) print(df[y].value_counts(normalizeTrue))输出可以看到客户年龄中位数在40岁左右正样本比例约11.3%。这些基础认知能帮你判断后面的特征工程是否合理。4.3 训练与调参真正的训练过程不需要人工太多干预GridSearchCV会自动搜索最优参数。不过我建议关注两个点交叉验证的折数项目里用的5折稳妥、以及随机种子random_state42保证每次训练结果可复现。另一个值得关注的点是源码在训练逻辑回归时用了StandardScaler做标准化但在训练树模型时没有做。这很合理因为逻辑回归这类线性模型对特征尺度敏感而树模型不受特征尺度影响。很多新手会不管三七二十一所有模型都做标准化其实没必要。4.4 预测结果落地训练完成后predict.py会读取一个“待预测客户名单”的CSV输出结果包含三列客户ID、预测概率、推荐状态。推荐状态的阈值默认是0.5但实际使用时我建议调低到0.3左右。为什么因为营销场景需要高召回率阈值降低意味着更多客户会被标记为“推荐外呼”虽然精确率会下降但捞回来的潜在客户更多。从成本角度看外呼一个客户多花几毛钱但捞回一个大额存款客户的收益可能是上千元。这个阈值怎么调取决于业务目标和成本结构源码把这个决策权留给了使用方这个设计很合理。# 阈值调整示例 recommended (pred_proba 0.3).astype(int)5. 常见问题与排查技巧实录5.1 解压路径与编码问题Windows下解压zip如果文件名包含中文很容易出现乱码。这个项目里主要文件都是英文命名问题不大。但如果你的zip包是从其他渠道下载的解压后出现乱码文件可以用7-Zip打开并选择“用UTF-8解码文件名”或者在macOS/Linux下解压基本能解决。5.2 sklearn版本导致的fit报错我复现时遇到过这样一个报错执行train_model.py时报“TypeError: fit() missing 1 required positional argument: y”。排查后发现是GridSearchCV的用法在不同版本间有差异scikit-learn 1.3以上对参数cv的要求更严格。解决方法是严格按照requirements.txt里的版本来装依赖不要用最新版。5.3 SMOTE过采样后的数据量SMOTE会把训练集的正样本扩充到和负样本一样多这样训练集规模会翻倍。如果你的机器内存不够训练XGBoost时可能会有点卡。我的建议是先把随机森林跑通再尝试XGBoost。实在卡得不行可以适当调低SMOTE的采样比例比如让正负样本比到1:2就够了不一定非要1:1。5.4 数据路径设置源码默认从data/raw/下读取原始数据如果你把zip包里的文件移到别的位置记得检查data_processing.py里路径是否对得上。我建议始终保持项目的完整目录结构不要单独把某个文件拷出来跑否则很容易出路径问题。5.5 常见问题速查表问题现象可能原因解决办法pandas读取CSV报错分隔符不是逗号原始数据用分号分隔读取时加 sep;SMOTE报错特征包含NaN值先运行fillna或dropna模型训练速度极慢独热编码后特征维度过高设置drop_firstTrue减少冗余列预测结果全是0类别不平衡且未处理启用SMOTE或class_weightbalanced导入模型失败joblib/scikit-learn版本不一致用保存模型时的同一环境加载模型6. 最后的实操心得与建议我在跑通整个项目之后最大的感受是一个机器学习项目能不能落地关键不在模型有多高级而在细节处理是否到位。比如pdays的999映射、duration字段的泄漏处理、类别不平衡下的评估指标选择这些代码里含水量非常低每一行都有实际意义。如果你只是需要交一个作业那直接跑通run_all.sh把评估结果截图放到报告里就够了。但如果你想让这个项目成为简历上的亮点我建议你多走一步把模型输出的概率结果结合业务成本画一条“收益曲线”。打个比方假定外呼一个客户的成本是5元一个成交客户带来的收益是500元那你可以根据模型预测的概率算出不同阈值下的净收益选一个最优阈值。这个分析写进报告里会明显拉开你和别人的差距。另外这个项目里用到的思路——特征工程、数据泄漏检测、不平衡处理、阈值选择——完全可以迁移到其他分类场景。我后来在用类似方法做用户流失预警时直接把特征工程部分改一改就用了省了非常多的时间。这也是为什么我推荐你把每个模块单独拎出来看而不是只当做一个黑盒去跑通。本文还有配套的精品资源点击获取
返回列表