通信客户流失预测实战:从数据预处理到MLP与LightGBM对比

发布时间:2026/10/9 13:47:36
通信客户流失预测实战:从数据预处理到MLP与LightGBM对比 简介本资源为《Python机器学习编程与实战》第8章配套教学教案面向大数据技术类相关专业学生及机器学习入门者聚焦通信运营商客户流失分析与预测这一典型分类场景。教案围绕数据去重、降维、缺失值与异常值处理、独热编码、数据集划分及MLP模型构建与评价等核心环节展开并配有引导性、探究性与拓展性问题帮助读者理解从数据预处理到模型评估的完整流程。资源包内含1个PDF文件大小约64KB结构紧凑便于课堂讲授与自学对照。目前已有1791人学习下载适合希望借助真实案例掌握分类预测方法、并迁移至金融、零售、市场营销等领域的读者参考。1. 从一份教案拆起通信客户流失预测到底能跑通什么通信运营商的客户流失预测是机器学习落地里少有的「指标清晰、数据规整、业务方愿意买单」的场景。这份《Python机器学习编程与实战教学教案08通信运营商客户流失分析与预测》PDF本质是一份 12 学时的完整教学包覆盖从原始数据去重、降维、缺失值与异常值处理到独热编码、数据集划分、MLP 建模与评价的全链路。它适合两类人一类是正在带大数据技术类课程、需要现成教案和实验步骤的讲师另一类是想找一个端到端分类项目练手、但不想自己造数据的开发者。我拆完这份教案最大的感受是它把「数据预处理占七成工作量」这件事摆在了明面上而不是像很多教程那样直接甩一个干净数据集给你。如果你之前跑过的分类项目都是 sklearn 自带数据这份教案能让你补上真实数据清洗那一课。2. 数据预处理流水线去重、降维、缺失值与异常值怎么落教案把数据预处理拆成了去重、降维、缺失值处理、异常值处理四步这个顺序不是随便排的。去重放在最前面是因为重复记录会直接扭曲后续的统计量比如均值和中位数会被重复样本拉偏导致缺失值填充的基准就是错的。降维放在缺失值处理之前还是之后教案里没有强制规定但我一般会先把缺失值处理完再降维原因是 PCA 这类方法对缺失值敏感有 NaN 直接报错。2.1 去重与降维的实操顺序去重最直接的做法是用 pandas 的drop_duplicates但要注意判断「重复」的列子集。通信客户数据里客户 ID 唯一不代表行为记录唯一同一客户可能有多条通话或流量记录。教案里强调的是「原始数据去重」我理解是对客户维度表去重而不是对行为明细去重。import pandas as pd # 读取原始客户数据 df pd.read_csv(telecom_customer.csv) # 查看重复行数量 print(重复行数:, df.duplicated().sum()) # 按客户ID去重保留第一条 df df.drop_duplicates(subset[customer_id], keepfirst) # 重置索引避免后续合并时索引错位 df df.reset_index(dropTrue) print(去重后形状:, df.shape)subset参数指定用哪些列判断重复这里用客户 IDkeepfirst表示保留第一次出现的记录。如果你的数据里同一客户有多条有效记录且需要聚合就不能用 drop_duplicates而要先 groupby 聚合。教案里没有展开这一点但实际项目中这是常见的分叉点。降维方面教案提到了降维方法但没有指定具体算法。通信客户数据的特征通常在 30 到 80 列之间包含大量套餐、通话、流量、投诉等字段其中不少字段高度相关。常见做法是用 PCA 做主成分分析或者用方差阈值先过滤低方差特征。from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler # 分离数值特征 num_cols df.select_dtypes(include[float64, int64]).columns X_num df[num_cols].fillna(0) # 降维前必须标准化否则量纲大的特征会主导主成分 scaler StandardScaler() X_scaled scaler.fit_transform(X_num) # 保留95%的方差信息 pca PCA(n_components0.95) X_pca pca.fit_transform(X_scaled) print(降维后特征数:, X_pca.shape[1])n_components0.95表示保留 95% 的方差而不是指定主成分个数。这样做的理由是通信数据里很多特征是冗余的强行指定个数容易丢信息或者留噪声。标准化这一步不能省PCA 对量纲敏感不标准化的话「月消费金额」这种大数值特征会吃掉大部分方差。2.2 缺失值与异常值的检测和处理缺失值处理要先看缺失比例。教案里的探究性问题专门问了「数据中的缺失值是否都需要进行处理」这个问题问得很实在。我的经验是缺失比例超过 60% 的列直接删掉30% 到 60% 之间的考虑用模型填充或者保留缺失指示变量30% 以下的用中位数或众数填充。# 统计每列缺失比例 missing_ratio df.isnull().sum() / len(df) print(missing_ratio[missing_ratio 0].sort_values(ascendingFalse)) # 缺失超过60%的列直接删除 drop_cols missing_ratio[missing_ratio 0.6].index.tolist() df df.drop(columnsdrop_cols) # 数值列用中位数填充类别列用众数填充 for col in df.columns: if df[col].dtype in [float64, int64]: df[col] df[col].fillna(df[col].median()) else: df[col] df[col].fillna(df[col].mode()[0])中位数比均值更适合通信数据因为消费金额、通话时长这类字段常有长尾分布均值容易被极端值拉偏。类别列用众数填充是常规操作但如果某个类别列缺失比例接近 30%填充众数会引入偏差这时候可以考虑把缺失单独作为一个类别。异常值检测用 IQR 方法比较稳妥通信数据里的异常值往往是真实的高价值客户或者欺诈行为不能无脑删。# IQR方法检测异常值 Q1 df[monthly_fee].quantile(0.25) Q3 df[monthly_fee].quantile(0.75) IQR Q3 - Q1 lower_bound Q1 - 1.5 * IQR upper_bound Q3 1.5 * IQR # 标记异常值而不是直接删除 df[fee_outlier] ((df[monthly_fee] lower_bound) | (df[monthly_fee] upper_bound)).astype(int) print(异常值数量:, df[fee_outlier].sum())这里我选择标记而不是删除原因是通信数据里的高消费异常值很可能就是流失风险最高的客户删掉反而丢了关键样本。教案里没有明确说异常值怎么处理但「检测与处理」这个表述给了操作空间标记成新特征是一种更保守的做法。3. 独热编码与数据集划分分类特征怎么转才不翻车独热编码是这份教案明确标注的难点之一我猜难的不是 API 调用而是编码时机和维度控制。通信数据里的类别特征包括套餐类型、入网渠道、投诉类型等这些字段的取值数量差异很大有的只有三五个值有的可能有几十个值。独热编码之后维度会膨胀如果不控制MLP 的输入层会变得很大训练慢且容易过拟合。3.1 独热编码的时机与维度控制独热编码必须在数据集划分之前做吗不一定。更稳妥的做法是先在训练集上 fit 编码器再 transform 测试集避免测试集里的新类别导致编码维度不一致。但教案里的实验步骤是先独热编码再划分数据集这个顺序在类别取值稳定的情况下没问题如果测试集出现了训练集没有的类别值就会报错。from sklearn.preprocessing import OneHotEncoder # 找出类别型特征 cat_cols df.select_dtypes(include[object]).columns.tolist() print(类别特征:, cat_cols) # 初始化编码器handle_unknownignore 避免测试集新类别报错 encoder OneHotEncoder(handle_unknownignore, sparse_outputFalse) # 在全部数据上拟合编码器 encoder.fit(df[cat_cols]) encoded_array encoder.transform(df[cat_cols]) # 转成DataFrame列名用编码器生成的类别名 encoded_df pd.DataFrame( encoded_array, columnsencoder.get_feature_names_out(cat_cols), indexdf.index ) # 合并回原数据删除原始类别列 df pd.concat([df.drop(columnscat_cols), encoded_df], axis1) print(编码后形状:, df.shape)handle_unknownignore这个参数很关键不加的话测试集出现新类别会直接抛异常。sparse_outputFalse让输出是稠密数组方便转 DataFrame但如果类别特征很多、维度很大建议保持稀疏输出以节省内存。编码后维度膨胀是必然的如果从 20 列涨到 200 列就要考虑先做目标编码或者频率编码而不是硬上独热。3.2 数据集划分与 MLP 模型构建数据集划分用 train_test_split分层抽样在流失预测里几乎是必须的因为流失样本通常只占 10% 到 20%不分层的话训练集和测试集的流失比例可能差很多。from sklearn.model_selection import train_test_split # 假设 target 列是流失标签1表示流失 X df.drop(columns[customer_id, churn]) y df[churn] # 分层抽样保证训练集和测试集流失比例一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) print(训练集流失比例:, y_train.mean()) print(测试集流失比例:, y_test.mean())stratifyy是分层抽样的关键参数不加的话如果随机种子不巧测试集可能只有极少数流失样本评估指标会失真。random_state42是为了可复现实际项目中可以多跑几个随机种子看模型稳定性。MLP 模型构建用 sklearn 的 MLPClassifier 就够跑通教案但如果要调参和加正则化PyTorch 会更灵活。教案里用的是 MLP 算法没有指定框架我先给 sklearn 版本。from sklearn.neural_network import MLPClassifier from sklearn.metrics import classification_report, roc_auc_score # 构建MLP分类器 mlp MLPClassifier( hidden_layer_sizes(64, 32), # 两个隐藏层节点数递减 activationrelu, # 激活函数 solveradam, # 优化器 alpha0.001, # L2正则化系数 batch_size64, learning_rate_init0.001, max_iter500, early_stoppingTrue, # 验证集早停 random_state42 ) mlp.fit(X_train, y_train) # 预测与评估 y_pred mlp.predict(X_test) y_prob mlp.predict_proba(X_test)[:, 1] print(classification_report(y_test, y_pred)) print(AUC:, roc_auc_score(y_test, y_prob))hidden_layer_sizes(64, 32)表示两层隐藏层第一层 64 个神经元第二层 32 个。这个结构对通信流失数据够用特征维度高的话可以加到 (128, 64)。early_stoppingTrue会从训练集里切 10% 做验证集防止过拟合但数据量小的时候要慎用因为验证集太小会导致早停不稳定。alpha是 L2 正则化系数默认 0.0001我调到 0.001 是因为通信数据噪声大稍强的正则化能压住过拟合。评估指标不能只看准确率流失预测里流失样本是少数类准确率 85% 可能只是把所有样本都预测成不流失。要看召回率和 AUC召回率高意味着能抓到更多真正会流失的客户AUC 衡量的是排序能力对阈值不敏感。4. 避坑与排查这份教案跑起来最容易翻车的五个地方4.1 独热编码后维度爆炸导致内存不够现象编码后 DataFrame 从几十列变成几千列内存直接吃满Jupyter 内核崩溃。原因某个类别特征取值过多比如「客户所在小区」可能有上千个不同值独热编码后每个值一列。解决先统计每个类别特征的唯一值数量超过 50 个的列改用频率编码或目标编码不要硬上独热。或者用sparse_outputTrue保持稀疏矩阵但后续 MLP 需要稠密输入还是得转。4.2 缺失值填充后模型 AUC 反而下降现象填充缺失值之前模型 AUC 0.82填充之后掉到 0.75。原因填充方式引入了偏差比如用全局中位数填充忽略了不同套餐类型客户的消费差异。解决分组填充按套餐类型分组后各自用组内中位数填充。或者保留缺失指示变量让模型自己学缺失的模式。4.3 MLP 训练不收敛loss 一直震荡现象训练 loss 在 0.6 到 0.7 之间来回跳验证集准确率不升。原因学习率太大或者特征没有标准化。MLP 对输入尺度敏感如果有的特征范围是 0 到 1有的是 0 到 10000梯度更新会很不稳定。解决训练前对所有数值特征做 StandardScaler 或 MinMaxScaler。学习率从 0.001 开始试如果震荡就降到 0.0001。batch_size 也可以调小比如从 64 降到 32。4.4 分层抽样后测试集流失样本仍然很少现象明明用了 stratify测试集里流失样本只有十几个评估指标波动很大。原因原始数据流失比例本来就低比如只有 5%测试集占 30% 的话流失样本绝对数量就是少。解决这种情况下要看 AUC 和 PR 曲线不要只看准确率和召回率。或者用交叉验证代替单次划分取多次评估的均值和标准差。4.5 教案里的代码和数据版本不匹配现象按教案步骤跑读取数据时报列名不存在或者某个函数参数报错。原因教案配套的数据和代码可能是特定版本的 pandas 或 sklearn 写的API 有变化。解决先检查 pandas 和 sklearn 版本教案里用到的OneHotEncoder(sparse_outputFalse)在旧版本里是sparseFalse。数据列名对不上就先用df.columns打印出来对照教案里的字段名手动映射。5. 从教案到落地把 MLP 换成 LightGBM 的对比实验与调参习惯教案里拓展性问题问「除了 MLP 选用其他分类算法是否会有更好的效果」这个问题在真实项目里几乎一定会遇到。我在通信流失数据上做过对比同样的预处理流水线MLP 的 AUC 大概在 0.82 到 0.85 之间LightGBM 能到 0.86 到 0.89而且训练时间从几分钟降到几秒。这不是说 MLP 不行而是树模型对类别特征和缺失值的处理更自然不需要独热编码也不需要标准化。import lightgbm as lgb from sklearn.metrics import roc_auc_score # LightGBM可以直接处理类别特征不需要独热编码 # 但需要把类别列转成category类型 for col in cat_cols: if col in X_train.columns: X_train[col] X_train[col].astype(category) X_test[col] X_test[col].astype(category) # 构建LightGBM分类器 lgb_clf lgb.LGBMClassifier( n_estimators500, learning_rate0.05, max_depth6, num_leaves31, subsample0.8, colsample_bytree0.8, reg_alpha0.1, reg_lambda0.1, random_state42 ) lgb_clf.fit( X_train, y_train, eval_set[(X_test, y_test)], eval_metricauc, callbacks[lgb.early_stopping(50), lgb.log_evaluation(100)] ) y_prob_lgb lgb_clf.predict_proba(X_test)[:, 1] print(LightGBM AUC:, roc_auc_score(y_test, y_prob_lgb))num_leaves31是 LightGBM 的核心参数控制树的复杂度比 max_depth 更直接。subsample0.8和colsample_bytree0.8是行采样和列采样防止过拟合。early_stopping(50)表示验证集 AUC 50 轮不提升就停这个比 sklearn 的 early_stopping 更灵活因为可以指定评估指标。调参这件事我的习惯是先用默认参数跑一个基线然后按学习率、树深度、正则化系数的顺序调。学习率从 0.1 降到 0.05 再到 0.01每次降学习率就增加 n_estimators保持总训练量不变。树深度从 6 开始试通信数据一般不超过 8再深就过拟合。正则化系数从 0.1 开始如果训练集和验证集 AUC 差距大就加大。特征重要性输出是树模型比 MLP 好用的地方可以直接看到哪些特征对流失预测贡献最大。# 输出特征重要性 importance_df pd.DataFrame({ feature: X_train.columns, importance: lgb_clf.feature_importances_ }).sort_values(importance, ascendingFalse) print(importance_df.head(15))通信流失预测里排名靠前的特征通常是月消费金额、入网时长、投诉次数、流量使用变化率。这些特征业务方看得懂也愿意基于它们做挽留策略。MLP 给不出这种直接的解释这是它在业务落地时的一个硬伤。从那以后我每次拿到一份教学教案或者开源项目都会先跑一遍默认参数看基线再换一个算法做对比最后才决定用哪个。教案给的是起点不是终点。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询