
1. 项目概述从赛题到实战的完整拆解“北京移动用户体验影响因素研究”这个题目一出来很多数学建模和大数据竞赛的参赛者可能会觉得既熟悉又头疼。熟悉的是它属于经典的“数据驱动型”问题在各类竞赛和实际业务中屡见不鲜头疼的是“用户体验”这个指标太虚了它不像销售额、点击率那样有明确的数值影响因素又盘根错节从网络信号到资费套餐从手机型号到用户行为如何从海量数据中抽丝剥茧建立有效的数学模型并最终用代码实现是横在队伍面前的一道高墙。这道题源自2022年MathorCup高校数学建模挑战赛大数据赛道的B题其核心价值在于它完美模拟了一个真实世界的数据科学项目闭环从模糊的业务问题定义到数据理解与清洗再到特征工程、模型构建与评估最后落地为可运行的代码。这不仅考验数学功底更考验工程化思维和解决实际问题的能力。简单来说这个项目要解决的是基于北京移动提供的大规模用户数据通常是脱敏后的信令数据、业务数据、用户属性数据等量化分析究竟是哪些因素在影响用户的“体验”并建立一个能够预测或评估用户体验水平的模型。对于参赛学生而言这是一个绝佳的练兵场对于数据分析从业者其思路和方法也具有直接的借鉴意义。接下来我将以一名数据科学实践者的视角彻底拆解针对该赛题“问题二”的建模方案与代码实现全过程分享从思路构建到代码落地的每一个关键细节和踩过的坑。2. 问题理解与核心目标定义任何数据项目的第一步也是最关键的一步就是准确理解问题。题目中的“问题二”通常会在“问题一”进行初步分析如描述性统计、关键指标计算的基础上要求进行更深入的建模分析。根据MathorCup的常见出题风格问题二很可能指向以下几个具体目标之一或组合1构建用户体验综合评价指数2识别影响用户体验的关键驱动因素特征重要性分析3建立用户体验的预测模型如分类好/中/差或回归评分预测。2.1 明确“用户体验”的量化方式这是建模的基石。原始数据中很可能没有直接的“用户体验得分”。我们需要利用现有数据构造一个代理指标。常见的思路有基于业务规则合成例如结合“网络掉线率”、“页面加载延时”、“语音通话质量差次数”等多个负面指标通过加权或阈值法合成一个“体验劣化指数”。基于用户行为推断例如将“用户离网风险评分”、“套餐降档意向”、“投诉次数”等作为体验差的反向指标。无监督学习聚类对用户多维度行为特征进行聚类将聚类结果如“高价值稳定用户”、“潜在流失用户”、“低活跃度用户”作为体验等级的标签。在本次方案中我们假设采用第一种方法定义一个用户体验得分。例如用户体验得分 100 - (a * 掉线率*100 b * 高延时占比*100 c * 投诉次数*权重)其中a, b, c为权重系数需要根据业务知识或后续分析确定。这一步的输出就是后续建模的目标变量。2.2 确定建模任务类型有了目标变量就可以定义任务如果我们将得分划分为“优、良、中、差”几个等级那么任务是一个多分类问题。如果我们直接使用连续得分那么任务是一个回归问题。如果我们只关心体验“好”与“不好”则可以转化为二分类问题。回归问题能提供更精细的预测但解释性可能稍弱分类问题更直观便于后续制定差异化策略。考虑到竞赛的全面性和展示性采用回归与分类结合的思路往往更出彩先用回归模型预测具体得分再根据得分阈值划分等级并利用分类模型如决策树、逻辑回归来增强关键因素的解释性。3. 数据预处理与特征工程实战原始数据通常是“脏”且“散”的。这部分工作会占据整个项目60%以上的时间直接决定模型性能的上限。3.1 数据清洗与整合假设我们拥有多张表用户基本信息表、月度业务量详单表、网络质量指标表、客户服务记录表。import pandas as pd import numpy as np # 1. 加载数据 user_info pd.read_csv(user_info.csv) business pd.read_csv(monthly_business.csv) network pd.read_csv(network_quality.csv) service pd.read_csv(service_record.csv) # 2. 关键字段类型转换与异常值处理 # 例如将日期字段转换为datetime类型 business[month] pd.to_datetime(business[month]) # 处理异常值对于业务量使用盖帽法Winsorization def winsorize(series, limits[0.05, 0.05]): return series.clip(lowerseries.quantile(limits[0]), upperseries.quantile(1-limits[1])) business[data_usage] winsorize(business[data_usage]) # 3. 数据合并以用户ID为主键关联各表信息 # 注意处理时间窗口例如分析特定月份的用户体验需关联该月及前几个月的特征 df user_info.merge(business, onuser_id, howleft) df df.merge(network, on[user_id, month], howleft) # 服务记录可能需要聚合例如计算每个用户每月的投诉次数 service_cnt service.groupby([user_id, service[create_time].dt.month]).size().reset_index(namecomplaint_count) df df.merge(service_cnt, left_on[user_id, df[month].dt.month], right_on[user_id, create_time], howleft) df[complaint_count].fillna(0, inplaceTrue)3.2 特征工程创造模型“燃料”这是体现建模者功力的地方。特征不能直接从原始字段拿来就用需要加工。统计特征对用户历史行为做滚动统计。例如计算过去3个月平均通话时长、流量使用量的环比增长率、业务量的波动性标准差。# 计算用户过去3个月平均流量使用量假设数据已按用户和时间排序 df[avg_data_3m] df.groupby(user_id)[data_usage].transform(lambda x: x.rolling(3, min_periods1).mean()) # 计算月度流量环比 df[data_usage_mom] df.groupby(user_id)[data_usage].pct_change()比值特征揭示结构信息。例如“夜间流量占比”、“国际长途通话时长占比”、“4G流量占全部流量的比例”。交互特征捕捉因素间的协同效应。例如“套餐流量余量”与“实际使用流量”的差值是否经常超量、“高价值用户”标签与“网络差区域”的交叉高价值用户在差区域的体验可能更敏感。分箱与编码对连续型特征如年龄、在网时长进行分箱等频、等宽然后进行标签编码或独热编码有助于线性模型捕捉非线性关系。时间序列特征如果数据时间跨度足够可以提取趋势、季节性等特征。注意特征工程后会产生大量特征必须进行特征选择避免维度灾难和过拟合。可以使用方差过滤移除方差过低的特征、相关性分析移除高度相关的特征、以及基于模型的方法如L1正则化、树模型的特征重要性。4. 建模方案设计与模型选型针对“影响因素研究”和“预测”的双重目标我们设计一个分层建模方案。4.1 第一层用户体验得分回归模型目标精准预测连续的用户体验得分。模型选型LightGBM/XGBoost首选。这类梯度提升树模型对表格数据效果极佳能自动处理非线性关系和特征交互且内置了特征重要性评估。计算效率高非常适合大数据竞赛。随机森林作为稳健的基准模型。解释性相对较好但预测精度和效率通常略逊于GBDT。线性回归/岭回归作为可解释性的对比。如果特征工程做得足够好如通过分箱、多项式特征线性模型也可能有不错的表现。为什么选LightGBM效率基于直方图的决策树算法训练速度快内存消耗低适合大数据场景。精度采用Leaf-wise生长策略在相同分裂次数下能获得更好的精度。功能全面直接支持类别特征、缺失值处理并提供丰富的特征重要性输出。4.2 第二层用户体验等级分类模型目标理解影响用户体验等级跃迁的关键规则。模型选型逻辑回归虽然简单但其系数可以直观解释为“特征变化一个单位用户体验为‘优’的概率的对数几率变化多少”非常适合因素分析。决策树/CART能生成清晰的“如果-那么”规则例如“如果用户在网时长24个月且月度投诉次数0那么体验等级为优的概率是85%”。这种规则业务人员非常喜欢。同样可以使用LightGBM分类如果追求更高的分类准确率。4.3 模型融合思路为了进一步提升预测的稳定性和鲁棒性可以考虑Stacking将LightGBM、随机森林、线性回归等作为第一层基模型将其预测结果作为新特征输入到一个第二层的元模型如线性回归或简单的神经网络中进行训练。这在竞赛中往往是提分利器。Blending将训练集划分为两部分一部分训练基模型另一部分用训练好的基模型预测并作为新特征与原始特征结合训练元模型。5. 代码实现详解与核心环节我们将以LightGBM回归模型为核心展示从特征准备到模型训练、评估、因素分析的全流程代码。5.1 环境准备与数据划分import lightgbm as lgb from sklearn.model_selection import train_test_split, KFold, GridSearchCV from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score import matplotlib.pyplot as plt import seaborn as sns # 假设df是经过充分特征工程后的DataFrame包含特征X和标签y用户体验得分 X df.drop(columns[user_id, month, exp_score]) # 移除ID、时间列和标签列 y df[exp_score] # 划分训练集和测试集时间序列数据需按时间划分此处假设为随机划分 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 对于LightGBM可以定义分类特征如果之前没有进行独热编码 categorical_features [city_district, phone_brand, plan_type] # 示例 for col in categorical_features: X_train[col] X_train[col].astype(category) X_test[col] X_test[col].astype(category)5.2 LightGBM模型训练与超参数调优直接使用默认参数往往不是最优的需要进行调优。# 1. 创建LightGBM数据集 train_data lgb.Dataset(X_train, labely_train, categorical_featurecategorical_features, free_raw_dataFalse) test_data lgb.Dataset(X_test, labely_test, referencetrain_data, categorical_featurecategorical_features, free_raw_dataFalse) # 2. 设置初始参数 params { boosting_type: gbdt, objective: regression, # 回归任务 metric: {l2, l1, rmse}, # 评估指标 num_leaves: 31, # 树的最大叶子数控制模型复杂度 learning_rate: 0.05, feature_fraction: 0.9, # 每次迭代随机选择90%的特征防止过拟合 bagging_fraction: 0.8, # 每次迭代随机选择80%的数据类似随机森林 bagging_freq: 5, verbose: 0, seed: 42 } # 3. 使用交叉验证进行初步调参和确定最佳迭代轮数 cv_results lgb.cv(params, train_data, num_boost_round1000, nfold5, stratifiedFalse, shuffleTrue, metricsrmse, early_stopping_rounds50, verbose_eval50, seed42) best_num_boost_rounds len(cv_results[rmse-mean]) print(fBest num_boost_round: {best_num_boost_rounds}) # 4. 使用GridSearchCV或Bayesian Optimization进行关键参数调优示例网格搜索 # 这里简化演示实际竞赛中可使用Optuna等更高效的库 param_grid { num_leaves: [15, 31, 63], learning_rate: [0.01, 0.05, 0.1], min_data_in_leaf: [20, 50, 100], } gbm lgb.LGBMRegressor(objectiveregression, metricrmse, n_estimatorsbest_num_boost_rounds) grid_search GridSearchCV(gbm, param_grid, cv5, scoringneg_root_mean_squared_error, verbose1, n_jobs-1) grid_search.fit(X_train, y_train) print(fBest parameters: {grid_search.best_params_}) # 5. 用最佳参数训练最终模型 best_params grid_search.best_params_ best_params.update({objective: regression, metric: rmse, verbose: -1}) final_model lgb.train(best_params, train_data, valid_sets[test_data], num_boost_roundbest_num_boost_rounds, callbacks[lgb.early_stopping(stopping_rounds30)])5.3 模型评估与结果分析训练完成后必须从多个维度评估模型。# 预测 y_pred final_model.predict(X_test, num_iterationfinal_model.best_iteration) # 评估指标 mse mean_squared_error(y_test, y_pred) rmse np.sqrt(mse) mae mean_absolute_error(y_test, y_pred) r2 r2_score(y_test, y_pred) print(fTest MSE: {mse:.4f}) print(fTest RMSE: {rmse:.4f}) print(fTest MAE: {mae:.4f}) print(fTest R2: {r2:.4f}) # 可视化预测值 vs 真实值散点图 plt.figure(figsize(10,6)) plt.scatter(y_test, y_pred, alpha0.5) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], r--, lw2) plt.xlabel(True Exp Score) plt.ylabel(Predicted Exp Score) plt.title(True vs Predicted Values) plt.show() # 可视化残差分布图 residuals y_test - y_pred plt.figure(figsize(10,6)) sns.histplot(residuals, kdeTrue) plt.xlabel(Residuals) plt.title(Distribution of Residuals) plt.axvline(x0, colorr, linestyle--) plt.show()一个好的模型其预测值与真实值散点图应紧密分布在对角线附近残差应近似服从均值为0的正态分布。5.4 影响因素分析洞察业务本质这是回答赛题“影响因素研究”的关键。# 1. 特征重要性增益 importance_gain pd.DataFrame({ feature: X_train.columns, importance_gain: final_model.feature_importance(importance_typegain) }).sort_values(importance_gain, ascendingFalse) # 2. 特征重要性分裂次数 importance_split pd.DataFrame({ feature: X_train.columns, importance_split: final_model.feature_importance(importance_typesplit) }).sort_values(importance_split, ascendingFalse) print(Top 10 features by gain:) print(importance_gain.head(10)) # 可视化 plt.figure(figsize(12,8)) sns.barplot(ximportance_gain, yfeature, dataimportance_gain.head(20)) plt.title(Top 20 Feature Importance (Gain)) plt.tight_layout() plt.show() # 3. SHAP值分析更精确地解释单个预测和整体特征影响 # 需要安装shap库: pip install shap import shap explainer shap.TreeExplainer(final_model) shap_values explainer.shap_values(X_test) # 摘要图展示特征影响的全貌 shap.summary_plot(shap_values, X_test, plot_typedot, max_display20) # 依赖图分析单个特征如何影响预测 # 例如分析“月度投诉次数”的影响 shap.dependence_plot(complaint_count, shap_values, X_test, interaction_indexNone)SHAP值是当前最受推崇的可解释性工具它能告诉我们每个特征对于每个预测样本的贡献值正或负并且满足一致性。从SHAP摘要图中我们可以清晰地看到哪些特征对模型输出影响最大以及这种影响的方向红色为高特征值蓝色为低特征值。6. 分类模型与规则提取在回归模型的基础上我们可以将预测得分分箱如0-60为差60-80为中80-90为良90-100为优然后训练一个分类模型来提取决策规则。from sklearn.tree import DecisionTreeClassifier, export_text, plot_tree from sklearn.metrics import classification_report, confusion_matrix # 1. 创建分类标签 y_train_class pd.cut(y_train, bins[0, 60, 80, 90, 100], labels[差, 中, 良, 优]) y_test_class pd.cut(y_test, bins[0, 60, 80, 90, 100], labels[差, 中, 良, 优]) # 2. 训练决策树分类器为了可解释性可以限制树深度 clf DecisionTreeClassifier(max_depth5, min_samples_leaf50, random_state42) clf.fit(X_train, y_train_class) # 3. 评估 y_pred_class clf.predict(X_test) print(classification_report(y_test_class, y_pred_class)) # 4. 可视化决策树 plt.figure(figsize(20,12)) plot_tree(clf, feature_namesX_train.columns, class_namesclf.classes_, filledTrue, roundedTrue, fontsize10) plt.show() # 5. 输出文本规则便于在论文中展示 tree_rules export_text(clf, feature_nameslist(X_train.columns)) print(tree_rules)从决策树规则中我们可以得到诸如“如果‘4G网络覆盖率’ 95% 且 ‘月度投诉次数’ 1则该用户有很高概率被分类为‘优’”这样的业务洞察这比单纯的特征重要性排名更具可操作性。7. 方案总结、常见问题与避坑指南回顾整个方案其核心链路是业务问题量化 - 数据清洗与特征创造 - 集成学习模型预测 - 可解释性工具分析。这套流程不仅适用于本赛题也适用于绝大多数用户画像、体验评估、风险预测等商业分析场景。7.1 常见问题与排查技巧实录在实际操作中你几乎一定会遇到以下问题问题现象可能原因排查与解决思路模型在训练集上表现极好在测试集上很差过拟合1. 模型过于复杂如树深度太大。2. 特征过多或存在大量噪音特征。3. 训练数据量不足或存在数据泄露。1. 增加正则化参数min_data_in_leaf,lambda_l1,lambda_l2减小num_leaves。2. 进行严格的特征选择方差过滤、相关性分析、基于模型的重要性筛选。3. 检查数据划分是否正确确保没有将未来信息泄露到训练中。使用交叉验证评估。模型性能提升遇到瓶颈1. 特征工程挖掘不够。2. 模型融合或集成不够。3. 数据质量本身存在天花板。1. 回头深入分析业务创造更有洞察力的特征如用户行为序列模式、社交网络特征。2. 尝试Stacking/Blending等模型融合技术。3. 检查目标变量定义是否合理数据是否存在系统性偏差。特征重要性最高的几个特征难以解释或不合常理1. 存在数据泄露目标变量信息直接或间接存在于特征中。2. 特征之间存在多重共线性导致重要性分配失真。3. SHAP依赖图显示复杂非线性关系。1.至关重要仔细检查特征构造过程确保没有使用任何未来的信息或与目标变量有直接因果关系的衍生变量。2. 计算特征间的相关系数矩阵移除高相关特征中的一个。3. 结合业务知识判断有时数据揭示的规律反直觉但真实。分类模型准确率高但某个类别如“差”的召回率极低样本不均衡。体验“差”的用户本就是少数。1. 使用过采样SMOTE或欠采样技术。2. 在模型训练时设置class_weight参数如balanced。3. 使用F1-score或AUC-PR曲线作为评估指标而不是单纯看准确率。代码运行速度慢特别是特征工程和调参阶段1. 数据量巨大。2. 循环操作过多。3. 网格搜索参数空间过大。1. 使用Pandas的向量化操作替代循环。对于超大样本可考虑使用Dask或Spark。2. 使用LightGBM的categorical_feature参数直接处理类别变量避免独热编码造成的维度爆炸。3. 使用贝叶斯优化如Optuna、Hyperopt替代网格搜索更高效地搜索超参数。7.2 独家避坑技巧与心得数据探索先行模型殿后在写任何模型代码之前花大量时间用pandas_profiling或手动进行数据可视化。了解每个字段的分布、缺失情况、异常值。对目标变量与关键特征的关系画散点图、箱线图。这个阶段的洞察会直接指导特征工程的方向。搭建可复现的Pipeline将数据预处理、特征工程、模型训练封装成函数或类。使用Pipeline和ColumnTransformersklearn。这不仅能保证训练和预测时数据处理的一致性更能让你快速尝试不同的特征组合。验证策略决定成败对于时间序列数据绝对不能随机划分必须按时间顺序划分例如用前8个月训练后2个月测试否则会严重高估模型性能。使用时间序列交叉验证TimeSeriesSplit。记录每一次实验使用MLflow、Weights Biases甚至一个简单的Excel表格记录每次尝试的特征组合、模型参数、评估指标。这能帮你系统性地寻找优化方向而不是盲目调参。业务解释优先最终评委或业务方关心的不是你的RMSE降低了0.001而是“我们发现了影响用户体验的三大关键因素分别是...建议采取...措施”。因此在模型达到可接受性能后应将重心转向SHAP分析、决策规则提取并准备清晰的可视化图表来讲好数据故事。最后把这个项目从赛题变成一个真正有意义的分析关键在于跳出数据看业务。模型指标是冰冷的但背后的每一个用户、每一次通话、每一兆流量都是真实的。你的工作就是通过数据和算法让这些沉默的信息开口说话为提升真实的用户体验找到那条数据驱动的路径。这个过程里对业务逻辑的不断追问和验证远比调出一个高分模型更有价值。