尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用户流失预测项目实战:从数据清洗到模型调参的完整流程

用户流失预测项目实战:从数据清洗到模型调参的完整流程 1. 这第五次作业我把它当成了一堂完整的项目课先说说这份作业的背景。其实我在接手“第5次作业”之前已经陆续做过几次课程作业了前四次基本是“照着模板写答案、提交完就完事”的状态。但第五次不一样这次的作业题目没有限定具体方向只给了一个很宽泛的任务完成一次端到端的分析演练。也就是说要从问题定义、数据获取、清洗、探索、建模到结果解读走完一整条链路。起初我有点懵因为太开放的任务反而不知道怎么下手但回头看恰恰是这份“自由度过高”的作业把我逼成了一名勉强合格的“业余全职分析师”。这篇博文我就把这次的完整过程拆开讲包括我是怎么选题的、数据从哪里来、踩过哪些坑、怎么处理脏数据和缺失值、模型迭代的时候被哪些细节卡住过以及最后做结果汇报时学到的一些经验。如果你也正在做类似的作业、或者刚入行想练手一个完整的数据分析项目这篇应该能给你一些可以直接照搬的套路也能帮你少走几次弯路。我的整体设计思路很简单先定一个能让评委一眼看懂的题目再找一份公开数据集用最主流的工具链Python Pandas scikit-learn走完流程最后把结论落到业务建议上。这个思路听起来很常规但真正动手之后你会发现每个环节都有无数个让你崩溃的细节。下面按步骤写。2. 选题与数据来源为什么我最终选了“用户流失预测”这个方向2.1 选题逻辑评分导向、数据可得性与业务可解释性开放题最常见的陷阱是“想搞个大新闻”比如上来就想预测股票、诊断疾病。这类题不是不行但对一门课程作业来说有三个硬伤第一数据不好拿第二模型效果很难做好容易让自己的整个分析过程看起来很业余第三也是最关键的如果业务背景太复杂评委很难快速理解你想干什么。所以我给自己定了三条选题标准数据要公开、结构清晰最好有一份现成的带标签的表格型数据问题要足够经典方便套用成熟的建模流程结论要能落地可以给出“我们该对哪类用户做点什么”这种可执行建议。综合比对下来我选了“电信用户流失预测”这个经典二分类问题。原因很简单一是公开数据集很成熟二是流失预测本身就是业务界经典场景通过用户属性、消费行为、服务订阅情况预测用户下个月会不会跑路既有一定的技术深度又有明确的业务价值。换句话说评委看完题目就知道你在做什么而且是认同这个分析方向的。2.2 数据集的获取与预审先盯住数据字典数据集选定了之后第一件事不是跑代码而是读数据说明数据字典。这一步很多人会忽视直接pd.read_csv()之后就开始画图结果后面做着做着才发现“这个特征不是我想的那个意思”。我这次先花了一个多小时把字段逐个过了一遍把每个变量的类型、取值范围、潜在缺失风险都列了一个Excel表。这个动作在后面帮了大忙。比如数据里有个TotalCharges字段看着像是数值型读进来之后发现是object类型。如果没看过说明很可能忽略掉。类似这种小问题后面单独说。总之数据预审不是浪费时间它决定了你后面写的代码是“一次过”还是要反复推翻重来。我的建议是拿到任何一份数据集先回答三个问题每条样本代表什么比如一个用户预测目标是什么比如用户是否流失有哪些特征每个特征是什么类型、单位、取值范围这三点理不清后面的特征工程基本就是瞎做。3. 环境准备与数据预处理的几个关键动作3.1 工具链的选型怎么选最简单的组合这次作业我用了Visual Studio Code Python 3.10核心库是Pandas、NumPy、Matplotlib、Seaborn和scikit-learn。没有上太复杂的工具原因很简单作业场景下效率和可复制性比炫技重要。如果你电脑环境还没有配好直接安装Anaconda也行它会帮你把大多数数据科学常用库一次配齐。有一个小建议最好在项目目录下建一个虚拟环境不要图省事把包都往全局装。课程作业做到后面各种库的版本冲突很常见比如numpy版本太新导致某些旧接口被移除这种问题排查起来非常耗时间。虚拟环境能帮你把“项目依赖”和“系统环境”隔离属于花五分钟省五小时的投入。创建虚拟环境很简单python -m venv .venv激活之后再安装依赖pip install pandas numpy matplotlib seaborn scikit-learn3.2 数据加载与快速体检一上来就建模的人才最危险数据加载很简单但加载完千万别急着进入建模环节。我用一个半小时做了一次完整的“数据体检”步骤基本固定看形状多少行多少列看每列类型和缺失率看数值型特征的分布均值、标准差、分位数看类别型特征的唯一值数量看目标变量的类别平衡度具体来说核心几条命令敲完之后我得到这样一些初步结论数据一共7043行、21列目标变量是Churn取值为Yes和No其中流失样本占比约26.5%。这个比例不算极端不平衡但也需要留意——后面评估模型时不能只看准确率否则模型全预测“不流失”也能拿到73%多的准确率看上去还不错实际啥也没学到。这个体检阶段最重要的产物是一张数据结构清单我后面每处理一个字段都有据可查。强烈建议大家把这五步养成肌肉记忆总时间成本不高但对全局的理解完全不一样。3.3 脏数据和缺失值的经典处理合集这个数据集最大的坑集中在几个字段我挨个说。第一个坑TotalCharges字段类型是字符串。因为数据在导出时可能混入了空字符串导致字段被识别为object。我的处理方式是先用pd.to_numeric()强制转换出错的地方变成NaN再统一填充。另外要注意它和MonthlyCharges、tenure之间存在天然关系TotalCharges ≈ MonthlyCharges × tenure。如果某行这三个字段对不上基本能反推数据质量有问题这是很实用的业务校验思路。第二个坑缺失值其实很少但处理方式仍然要讲究。检查下来发现只有TotalCharges存在11个空值占比极低。对这种情况直接删除这几行就行不需要做复杂插补。因为只有11个样本插补引入的偏差可能大于直接删除的影响。这个思路要记住缺失值处理不是越高级越好而是要看缺失比例和业务含义。缺失很少直接删缺失较多就得分析缺失机制再决定用均值/中位数/模型预测哪种方式填充。第三个坑类别型变量里藏着“误导性数字”。有些特征看起来是数字其实是类别编码比如SeniorCitizen取值为0和1表示是否老年人根本不是数值大小意义上的“等级”。如果直接拿去算距离模型会把它当成连续变量逻辑上就错了。处理方式是按分类型变量处理该独热编码就独热编码。这个点看起来很简单但实际做作业时会有一大批人踩坑。第四个坑tenure字段看起来是连续值但分布是“高门槛型”。新用户集中在前几个月老用户则缓慢递减。这种分布直方图很容易被“均值±标准差”概括掉但流失分析里恰恰是用户生命周期早期最值得关注所以后面特征工程时我把它做了分段处理。3.4 特征工程的取舍独热编码、特征衍生与陷阱排除基线模型可以直接用原始特征跑但想要效果好一点特征工程还是很值的。我从两个方向做把类别型变量做独热编码包括InternetService、Contract、PaymentMethod等把tenure这种有业务含义的量做成用户生命周期分段比如“未满一个月”“1到12个月”“1年到2年”“2年以上”保留具体数值的同时增加一个可解释的分组。这里也踩了一个小坑数据集里有一组与“补充服务”相关的字段比如OnlineSecurity、DeviceProtection、TechSupport等它们的取值为Yes/No/No internet service。粗看是三类其实“No internet service”和“No”在业务上完全不同——前者是因为没开通网络服务后者是开通了但没买补充包。如果直接独热编码会让模型认为这是三个独立状态反而丢失了层级关系。我是手动把“No internet service”跟“No”合并或者单独衍生一个标志位“是否开通了某种补充服务”只要是合理的业务理解模型效果和可解释性都会更好。特征工程的最终结果是从20个原始特征扩展到六十多个模型输入列。听起来数量变多了但大多数是独热编码产生的0/1列不增加太多计算负担。4. 建模与调参从基线模型到“效果看得过去”4.1 数据集划分与交叉验证别让模型“偷看答案”在建模前我严格按70%训练、30%测试的比例划分数据并且固定了随机种子。这样后面的每次实验都可以直接对比不会因为随机划分导致结果差异。这一步也是很多初学者的“隐藏翻车点”特征工程时必须先划分训练集和测试集再在训练集上做编码和填充否则测试集的信息会通过“全局统计量”泄露进模型。我这次的所有独热编码都用fit_transform在训练集上完成、再用transform处理测试集确保测试集是完全没被“见过”的。除了单次划分我还加了5折交叉验证来评估模型稳定性避免“某一刀切得好导致虚高”的情况。交叉验证的作用是用多份不同子集轮流当验证集最终得分是所有折的平均值能更真实地反映模型泛化能力。4.2 三个模型的横向对比逻辑回归、随机森林、XGBoost基线实验我一次跑了三个模型——逻辑回归、随机森林、XGBoost。这里先不追求极致调参先看它们在默认参数下的表现选出一个有潜力的方向再深入。关键评估指标我选了ROC-AUC、召回率和F1分数。原因前面提过数据本身有26.5%的流失率准确率容易被“多数类”主导。流失预测业务里我们更关心的是“真正流失的人里模型能召回多少”。比如100个会流失的用户模型只找到了30个那召回率就是0.3这个数字比准确率有价值得多。from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier from sklearn.model_selection import cross_val_score models { LogisticRegression: LogisticRegression(max_iter1000, random_state42), RandomForest: RandomForestClassifier(n_estimators200, random_state42), XGBoost: XGBClassifier(eval_metriclogloss, random_state42) } for name, model in models.items(): scores cross_val_score(model, X_train, y_train, cv5, scoringroc_auc) print(f{name}: {scores.mean():.4f} (±{scores.std():.4f}))第一轮结果如下模型ROC-AUC交叉验证逻辑回归0.8421随机森林0.8314XGBoost0.8467说实话这个结果出乎我意料。我本来以为随机森林会碾压逻辑回归但实际上在这个数据集上树模型的优势没有体现出来。原因可能是很多特征本身就是类别型变量经过独热编码之后特征空间比较稀疏逻辑回归在这种场景下表现并不差。XGBoost略微领先但和逻辑回归相差不大这让我意识到一个问题先跑基线模型再下结论非常重要凭经验拍脑袋猜哪个模型最好往往会被现实打脸。4.3 类别不平衡的处理不做其实也行但做了更稳虽然流失样本占比26.5%还算能接受但我还是试了一下类别不平衡的经典解决方案——调整类别权重和过采样。scikit-learn里很多模型直接支持class_weightbalanced这是最省事的做法。我对比了一组实验逻辑回归默认配置 vsclass_weightbalanced。结果发现ROC-AUC小幅提升但召回率显著提高。代价是精确率下降也就是模型更倾向于“宁杀错不放过”会把一些本来不流失的用户也预测成流失。在真实业务里这意味着运营团队需要花更多精力去触达“假阳性”用户。权衡之后我觉得在作业场景下“召回率提升”带来的分析价值大于“精确率下降”带来的损失因为我们的目标是识别潜在流失用户哪怕误报一些也还有挽回空间。所以最终选用了平衡版本。4.4 调参与验证网格搜索的收获与陷阱接下来针对XGBoost做了简单的网格搜索调参重点关注n_estimators、max_depth、learning_rate和subsample。因为计算资源有限我没有做全网格搜索而是按“先粗后细”的思路分两轮第一轮粗调范围param_grid { n_estimators: [100, 200, 300], max_depth: [3, 4, 5], learning_rate: [0.01, 0.05, 0.1], subsample: [0.7, 0.8, 1.0] }第二轮在最优参数的周围细调比如learning_rate换成0.03和0.07max_depth换成4和6。这种做法的效率远高于一次性把所有参数组合都跑完而且更容易找到“局部甜点”。调完参数后测试集ROC-AUC稳定在0.849左右比默认参数略好一点。说实话提升幅度不大但这个过程让我学到了一个很重要的点在这个数据集上特征与业务理解带来的收益远大于模型结构和参数微调带来的收益。这也解释了为什么很多人拿到一份结构复杂但是特征无区分度的数据怎么调都上不去。反过来如果先想清楚业务逻辑、做几个高质量特征可能一个最普通的逻辑回归就能打赢XGBoost。5. 模型解释与结果落地不只是跑个准确率交差5.1 从“黑盒”到“可解释”特征重要性怎么读作业要求写分析结论所以模型不能只有预测能力还要能回答“哪些因素在影响流失”。这里我用了两种方式树模型自带的feature_importances_看哪些特征对分裂的贡献大逻辑回归的系数大小和方向作为交叉验证。两者的结论高度一致Contract特别是按月签约、tenure用户在网时长和MonthlyCharges月消费金额是最重要的三个因素。具体来说长期合同用户的流失概率显著低于按月用户在网时间越长流失风险越低月消费越高流失风险越高这很可能是因为高消费用户对价格更敏感、预期更高一旦服务体验不达标更容易离开。这些结论不需要复杂的SHAP值分析就能得到而且放到业务语境里完全说得通。我第一次跑出这些系数的时候特别有“做分析的感觉”——并不是用一个复杂的模型去炫技而是真正回答了业务方最关心的“哪些人要走、为什么走”的问题。5.2 目标群体画像把模型结果翻译成业务动作模型跑完之后我还没有直接写结论而是做了一步“目标群体画像”。我按模型的预测概率把测试集里预测流失概率排名前20%的样本筛出来然后统计这批用户在各特征上的分布。结果非常直观这部分用户的画像大概是大部分没有签长期合同而是按月付费在网时间普遍小于12个月月消费金额处于中高水平很多没有开通在线安全服务、技术支持等附加服务。对照这个画像可以提出一个具体的运营建议在用户入网的前三个月建立流失预警机制通过赠送附加服务或提供套餐折扣来提升粘性。这比一句“我们要降低流失率”有价值得多。5.3 关于汇报材料的一个体会最后交作业的时候我把所有代码整理成一个带注释的Notebook再额外写了一份两页的报告核心结构只有四个部分问题背景与目标数据处理思路模型效果对比业务建议。说实话汇报材料不在多而在逻辑清晰。评委最怕看到的是“代码堆了一堆但不知道你想表达什么”。我这次写报告时每个图表都配上了一句“所以这意味着什么”而不是单纯展示图长什么样。这一点我觉得是这次作业里最有收获的地方之一。6. 这五次作业走下来我记住的几件事这次“第5次作业”从头到尾做完回头总结几个实操层面的经验按优先级排序写一下。数据质量决定模型上限。这句话听了无数遍但这次是真的亲手验证了。TotalCharges一个字段的类型问题、No internet service的合并策略哪怕只改一处最后AUC都会有所变化。数据清洗阶段多花一小时后面建模阶段就能少折腾十小时。基线模型一定要先跑。不要一上来就上深度学习或者各种AutoML先用最普通的模型把流程跑通拿到一个基准分再去逐步优化。这样做既能在有限时间内完成作业也能帮你建立对数据的判断——“这个数据大概能做到什么水平”。特征工程要有业务逻辑支撑。不要为了造特征而造特征。你在做的是“用户流失预测”那就应该从用户生命周期、消费习惯、服务体验这些角度去想哪些信息可能有关系而不是让模型自己从一堆噪声里挑信号。评估指标一定要贴合任务目标。这个流失预测数据如果用准确率评估73%多就能“自我感觉良好”但实际对业务毫无贡献。换成召回率、F1、ROC-AUC之后模型的真实水平才显现出来。关键不是代码多花哨而是知道“该看什么数字”。做作业不等于做研究好讲清楚比做得复杂更重要。我这次没有用任何深度学习模型全程就是三个经典模型跑对比但最后报告拿到的反馈比前四次都正向。因为整个分析链路是可信的、可复现的、有业务落地的。只要逻辑清晰、结论明确朴素的方法一样能打动听众反而堆砌复杂模型容易让结果无法解释自己和评委都看得一头雾水。最后再分享一个小习惯每次跑实验之前我都会在代码开头留一个“实验说明”的注释写清楚这次实验想验证什么、改了哪些变量、预期是什么效果。后续回头复盘的时候这些注释就是一个很清晰的实验日志。对这个项目来说它帮助我在写最终报告时能快速回忆起每一次尝试的动机和结果而不是面对一堆没有名字的输出文件发呆。如果你也在做类似的项目作业强烈建议试一试。
返回列表