1. 从E题聊起:为什么这道题值得单独拿出来复盘
华为杯研究生数学建模竞赛的E题,历来是整张卷子里“看起来最像业务题、做起来最像工程题”的那一类。2026年的E题延续了这个传统——它不会给你一个纯粹的数学优化目标让你去推公式,也不会像A题那样上来就是物理建模的硬骨头,而是把一堆真实场景下的数据丢给你,让你从里面挖出规律、建出模型、给出决策建议。说白了,它考的不是你会不会解偏微分方程,而是你能不能像一个真正的数据分析师那样,把一团乱麻的数据理清楚,然后讲出一个有说服力的故事。
我之所以想单独把E题拿出来分享,是因为这道题的“坑”特别有代表性。很多队伍拿到题目之后第一反应是套机器学习模型,XGBoost、随机森林、LSTM往上堆,结果发现特征工程没做好,数据清洗没做干净,最后模型跑出来的结果连自己都不敢信。还有的队伍在可视化上花了大量时间,图表做得花里胡哨,但评委一看就知道核心逻辑没跑通。E题的评分标准里,数据预处理的合理性、模型选择的依据、结果的可解释性这三块占了大头,而不是看你用了多高级的算法。
这篇文章适合谁看?如果你是第一次参加华为杯、对E题这种“数据驱动型”题目没有头绪,那这篇内容可以帮你少走至少三天的弯路。如果你已经做过一两届,但每次都在数据清洗和特征选择上卡壳,那这里面的实操细节和避坑经验对你同样有用。我会从赛题拆解、数据预处理、特征工程、模型选型、可视化呈现、论文写作六个维度,把E题的完整打法拆开来讲,中间穿插我自己踩过的坑和总结出来的技巧。
提示:E题的官方数据通常包含多个CSV文件,字段命名不规范、缺失值隐蔽、时间格式混乱是常态。不要指望拿到数据就能直接喂给模型,前期的数据审计工作至少占整个赛程的40%。
2. 赛题拆解:E题到底在考什么能力
2.1 从“业务问题”到“数学问题”的翻译过程
E题的题干通常是一段业务场景描述,比如“某平台需要根据用户行为数据预测流失风险”或者“某城市需要根据交通流量数据优化信号灯配时”。很多队伍一上来就开始想用什么模型,这是典型的顺序错误。正确的做法是先把业务语言翻译成数学语言:预测流失风险翻译过来就是“二分类问题,目标变量是是否流失,特征变量是用户行为指标”;优化信号灯配时翻译过来就是“带约束的优化问题,决策变量是各相位时长,目标函数是平均等待时间最小化”。
这个翻译过程决定了你后续所有工作的方向。我见过一个队伍,题目要求“评估不同营销策略的效果”,他们直接做了一个聚类分析,把用户分成几群就交差了。问题是,聚类只能告诉你“用户分几类”,不能告诉你“哪种策略对哪类用户有效”。正确的做法应该是构建一个因果推断框架,或者至少做一个A/B测试的统计分析。翻译错了,后面全错。
2.2 E题常见的三类问题结构
根据我做过和看过的E题,大致可以归为三类。第一类是预测类,给你历史数据,让你预测未来某个指标的值,比如销量、流量、故障率。这类题的核心是时间序列建模或回归建模,重点在于特征构造和验证集划分。第二类是分类类,给你带标签的数据,让你判断新样本属于哪一类,比如信用评级、疾病诊断。这类题的核心是类别不平衡处理和模型可解释性。第三类是优化类,给你约束条件和目标函数,让你找最优解,比如资源分配、路径规划。这类题的核心是问题建模的准确性和求解算法的效率。
2026年E题我拿到手之后,第一件事就是判断它属于哪一类。从题干描述和数据字段来看,它更偏向“预测+优化”的混合结构:先根据历史数据预测某个关键指标,再基于预测结果做资源分配决策。这种混合结构在近几年的E题里越来越常见,因为它更贴近真实业务场景——业务决策从来不是单一任务,而是“预测-决策”的链条。
2.3 评分标准里的隐藏信号
华为杯的评分标准虽然不会提前公布,但从历年优秀论文的共性来看,有几个信号非常明显。第一,数据预处理的篇幅占比很高。优秀论文通常会用2-3页详细描述缺失值处理、异常值检测、数据标准化等步骤,而不是一笔带过。第二,模型对比是必须的。你不能只用一个模型跑出结果就完事,至少要有2-3个基准模型的对比实验,说明为什么最终选择了某个模型。第三,结果的可解释性比精度更重要。一个准确率85%但能清楚解释每个特征贡献度的模型,往往比准确率90%但黑箱的模型得分更高。
注意:E题不是算法竞赛,评委更看重你的分析逻辑是否严密,而不是你的模型是否刷新了SOTA。把精力花在“为什么这样做”上,比花在“用什么高级模型”上回报率高得多。
3. 数据预处理:E题最容易被低估的40%工作量
3.1 数据审计:先看清楚你手里有什么
拿到数据之后,不要急着写代码。先做一轮完整的数据审计,搞清楚以下几个问题:每个文件有多少行多少列?字段类型是什么?缺失值比例是多少?有没有重复记录?时间字段的格式是否统一?类别型字段的取值分布是什么样的?
我习惯用一段固定的Python代码来做初步审计,大概长这样:
import pandas as pd import numpy as np def audit_data(df, name): print(f"=== {name} ===") print(f"Shape: {df.shape}") print(f"Columns: {list(df.columns)}") print(f"Missing ratio:\n{df.isnull().mean().sort_values(ascending=False)}") print(f"Duplicates: {df.duplicated().sum()}") print(f"Dtypes:\n{df.dtypes}") print(f"Describe:\n{df.describe(include='all')}") # 对每个CSV文件调用 for file in ['train.csv', 'test.csv', 'user_info.csv']: df = pd.read_csv(file) audit_data(df, file)这段代码看起来简单,但它能帮你快速定位问题。比如有一次我发现某个字段的缺失率高达70%,但题干里根本没提这个字段,后来才意识到这个字段是“可选填写”的,直接删掉比强行填充更合理。
3.2 缺失值处理:不是所有缺失都该填
缺失值处理是E题里最容易踩坑的地方。很多队伍一看到缺失值就无脑用均值填充或者中位数填充,这是非常危险的做法。缺失值的处理方式取决于缺失机制:完全随机缺失可以用均值/中位数填充;随机缺失可以用模型预测填充;非随机缺失则需要分析缺失原因,甚至把“是否缺失”本身作为一个特征。
举个例子,如果用户收入字段有大量缺失,而且缺失的用户大多是低活跃度用户,那这个缺失就不是随机的。你直接填均值,相当于给低活跃用户强行安了一个平均收入,会严重扭曲数据分布。更好的做法是:先分析缺失用户和非缺失用户在其它特征上的差异,如果差异显著,就把“收入是否缺失”作为一个二值特征加入模型,同时用分组均值填充。
# 分析缺失机制 df['income_missing'] = df['income'].isnull().astype(int) print(df.groupby('income_missing')[['age', 'activity_score']].mean()) # 分组填充 df['income'] = df.groupby('income_missing')['income'].transform( lambda x: x.fillna(x.median()) )3.3 异常值检测:Z-score和IQR的适用场景
异常值检测常用的方法有Z-score和IQR两种。Z-score假设数据服从正态分布,适合处理近似正态的连续变量;IQR不依赖分布假设,适合处理偏态分布的数据。E题的数据往往混合了这两种情况,所以我的做法是:对每个连续变量先画直方图看分布,正态的用Z-score,偏态的用IQR。
但要注意,异常值不一定要删除。有些异常值是真实业务场景下的极端情况,比如“双十一”当天的流量峰值,你把它删了反而丢失了重要信息。正确的做法是:先标记异常值,分析它们的业务含义,再决定是删除、截断还是保留。我通常会在论文里专门用一小节讨论异常值处理策略,这本身就是加分项。
3.4 时间字段处理:E题的时间陷阱
E题的数据里几乎必然包含时间字段,而且格式往往不统一。有的用“2026-01-15”,有的用“2026/1/15”,有的用时间戳。第一步是统一格式,第二步是提取时间特征:年、月、日、星期、是否节假日、是否周末。这些衍生特征对预测类问题特别重要。
df['date'] = pd.to_datetime(df['date'], format='mixed') df['year'] = df['date'].dt.year df['month'] = df['date'].dt.month df['dayofweek'] = df['date'].dt.dayofweek df['is_weekend'] = df['dayofweek'].isin([5, 6]).astype(int)还有一个容易被忽略的点:时间序列的排序。如果你的数据是按时间顺序排列的,做交叉验证时不能用随机划分,必须用时间序列划分(TimeSeriesSplit),否则会造成数据泄露,模型在验证集上的表现会虚高。
4. 特征工程:决定E题上限的关键环节
4.1 特征构造的三种思路
特征工程是E题拉开差距的核心环节。同样的数据,不同队伍构造出来的特征可能天差地别。我总结下来有三种思路:统计聚合、业务衍生、交互组合。
统计聚合是指对某个实体(比如用户、商品、地区)计算统计量,比如均值、最大值、最小值、标准差、分位数。比如用户行为数据,你可以计算每个用户的“近7天平均活跃时长”“近30天最大单次消费金额”等。业务衍生是指根据业务逻辑构造特征,比如“消费金额/收入”得到消费率,“点击次数/曝光次数”得到点击率。交互组合是指把两个或多个特征做运算,比如“年龄×收入”“活跃度/注册天数”。
4.2 特征选择:过滤法、包裹法、嵌入法
特征不是越多越好。E题的数据字段可能几十上百个,但真正有用的可能只有十几个。特征选择方法有三类:过滤法(方差阈值、卡方检验、互信息)、包裹法(递归特征消除RFE)、嵌入法(Lasso回归、树模型的特征重要性)。
我通常先用过滤法快速筛掉方差极低或与目标变量相关性极低的特征,再用嵌入法(比如随机森林的feature_importances_)做精细筛选。包裹法计算量大,E题时间紧张的时候不建议用。
from sklearn.ensemble import RandomForestClassifier rf = RandomForestClassifier(n_estimators=100, random_state=42) rf.fit(X_train, y_train) importance = pd.DataFrame({ 'feature': X_train.columns, 'importance': rf.feature_importances_ }).sort_values('importance', ascending=False) print(importance.head(20))4.3 类别不平衡:E题分类问题的隐形杀手
如果E题是分类问题,类别不平衡几乎必然出现。比如“流失用户”可能只占总用户的5%,“违约用户”可能只占3%。这时候如果你直接用准确率作为评价指标,模型只要全部预测为“非流失”就能达到95%的准确率,但这显然没有意义。
处理类别不平衡的方法有:重采样(过采样少数类、欠采样多数类)、合成采样(SMOTE)、调整类别权重(class_weight='balanced')、更换评价指标(F1-score、AUC-ROC)。我通常优先用调整类别权重的方法,因为它不改变数据分布,实现简单。如果效果不好,再尝试SMOTE。
from imblearn.over_sampling import SMOTE smote = SMOTE(random_state=42) X_resampled, y_resampled = smote.fit_resample(X_train, y_train) print(f"Before: {y_train.value_counts()}") print(f"After: {y_resampled.value_counts()}")提示:SMOTE只能在训练集上做,绝对不能在测试集上做。否则测试集里混入了合成样本,评估结果完全失真。这个坑每年都有队伍踩。
4.4 特征缩放:不是所有模型都需要
特征缩放(标准化、归一化)对某些模型是必须的,对另一些模型则无所谓。必须做缩放的模型:KNN、SVM、神经网络、逻辑回归(带正则化时)。不需要做缩放的模型:决策树、随机森林、XGBoost、LightGBM。E题里如果你用了多种模型做对比,记得对需要缩放的模型单独处理,不要一刀切。
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 注意:fit只在训练集上做,测试集用transform5. 模型选型与验证:E题不是算法竞赛
5.1 从基线模型开始,不要一上来就上深度学习
很多队伍觉得E题要用“高级”模型才能拿高分,一上来就搭LSTM、Transformer。但E题的数据量通常不大,深度学习很容易过拟合,而且训练时间长、调参困难。我的建议是:先跑一个逻辑回归或决策树作为基线,看看效果如何。如果基线模型已经能达到不错的性能,说明特征工程做到位了,再尝试更复杂的模型看能否提升。
基线模型还有一个好处:它能帮你快速验证数据预处理和特征工程是否正确。如果逻辑回归跑出来AUC只有0.5,那大概率是数据处理出了问题,而不是模型不够复杂。
5.2 树模型在E题中的统治地位
从历年E题优秀论文来看,XGBoost、LightGBM、随机森林是出现频率最高的模型。原因很简单:E题的数据通常是表格型数据,树模型在表格数据上的表现往往优于深度学习;树模型能自动处理特征交互,不需要太多人工构造;树模型有特征重要性输出,可解释性好。
import lightgbm as lgb from sklearn.model_selection import cross_val_score model = lgb.LGBMClassifier( n_estimators=500, learning_rate=0.05, max_depth=6, num_leaves=31, random_state=42 ) scores = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc') print(f"AUC: {scores.mean():.4f} (+/- {scores.std():.4f})")5.3 交叉验证:E题验证集划分的正确姿势
交叉验证是评估模型泛化能力的标准方法,但E题里要注意两点。第一,如果是时间序列数据,必须用TimeSeriesSplit,不能用普通的KFold,否则未来信息会泄露到训练集。第二,如果是分组数据(比如同一用户的多条记录),必须用GroupKFold,保证同一组的样本不会同时出现在训练集和验证集。
from sklearn.model_selection import TimeSeriesSplit, GroupKFold # 时间序列 tscv = TimeSeriesSplit(n_splits=5) # 分组 gkf = GroupKFold(n_splits=5)5.4 超参数调优:网格搜索还是贝叶斯优化
超参数调优的方法有网格搜索、随机搜索、贝叶斯优化。网格搜索适合参数少、取值范围小的情况;随机搜索适合参数多、计算资源有限的情况;贝叶斯优化适合需要精细调优的情况,但实现复杂度高。E题时间紧张,我通常用随机搜索快速找到大致范围,再在小范围内做网格搜索。
from sklearn.model_selection import RandomizedSearchCV param_dist = { 'n_estimators': [100, 300, 500, 800], 'learning_rate': [0.01, 0.05, 0.1], 'max_depth': [3, 5, 7, 9], 'num_leaves': [15, 31, 63] } search = RandomizedSearchCV( model, param_dist, n_iter=30, cv=5, scoring='roc_auc', random_state=42, n_jobs=-1 ) search.fit(X_train, y_train) print(search.best_params_)6. 可视化与论文呈现:让评委一眼看懂你的逻辑
6.1 可视化不是越多越好,而是越准越好
E题的论文里,可视化图表是评委快速理解你思路的窗口。但很多队伍陷入了“图表越多越好”的误区,放了十几张图,结果每张图都没说清楚。我的原则是:每张图都要有明确的表达目的。流程图用来说明技术路线,热力图用来说明特征相关性,折线图用来说明时间趋势,散点图用来说明变量关系,混淆矩阵用来说明分类效果。
用ECharts或者Matplotlib都可以,关键是配色要统一、标注要清晰、图例要完整。我习惯用一套固定的配色方案,让整篇论文的图表风格一致,看起来更专业。
6.2 论文结构:从摘要到结论的完整链条
华为杯论文的结构通常是:摘要、问题重述、问题分析、模型假设、符号说明、模型建立与求解、模型检验、灵敏度分析、模型评价与推广、参考文献、附录。E题的论文要特别注意问题分析和模型检验两部分。问题分析要展示你对业务问题的理解深度,模型检验要展示你对模型可靠性的验证。
摘要是最重要的部分,评委可能只花5分钟看摘要就决定是否仔细阅读全文。摘要要包含:问题背景一句话、每个问题的建模方法、主要结果、模型评价。不要写“本文研究了...”这种空话,直接写“针对问题一,本文构建了...模型,得到...结果”。
6.3 灵敏度分析:E题加分项
灵敏度分析是很多队伍忽略的环节,但它恰恰是展示你模型鲁棒性的好机会。简单来说,就是改变某个关键参数或输入变量,观察模型输出如何变化。比如你做了一个预测模型,可以分析“当训练集比例从70%变为80%时,预测精度如何变化”;或者“当某个特征的缺失率增加10%时,模型性能下降多少”。
# 简单的灵敏度分析示例 from sklearn.metrics import roc_auc_score results = [] for train_size in [0.6, 0.7, 0.8, 0.9]: split_idx = int(len(X) * train_size) X_tr, X_te = X[:split_idx], X[split_idx:] y_tr, y_te = y[:split_idx], y[split_idx:] model.fit(X_tr, y_tr) pred = model.predict_proba(X_te)[:, 1] auc = roc_auc_score(y_te, pred) results.append({'train_size': train_size, 'auc': auc}) print(pd.DataFrame(results))7. 那些年我在E题上踩过的坑
7.1 数据泄露:最隐蔽也最致命的错误
数据泄露是指训练集里包含了测试集的信息,导致模型在验证集上表现虚高,但实际预测时一塌糊涂。常见的泄露场景包括:在划分训练测试集之前做了标准化、在特征选择时用了全部数据、时间序列数据用了随机划分。我踩过最惨的一次是:先对全部数据做了PCA降维,再划分训练测试集。结果模型在验证集上AUC 0.95,实际提交后只有0.6。后来才发现PCA降维时用到了测试集的信息。
注意:所有数据预处理操作(标准化、PCA、特征选择)都必须在训练集上fit,然后transform测试集。顺序绝对不能反。
7.2 过拟合:模型在训练集上完美,在测试集上崩溃
过拟合是E题里另一个高频问题。表现是:训练集准确率99%,验证集准确率70%。原因通常是模型太复杂、特征太多、训练数据太少。解决方法有:减少模型复杂度(降低树深度、减少叶子节点数)、增加正则化(L1/L2)、增加训练数据(如果可能)、早停(Early Stopping)。
# LightGBM早停 model = lgb.LGBMClassifier(n_estimators=2000, learning_rate=0.05) model.fit( X_train, y_train, eval_set=[(X_val, y_val)], eval_metric='auc', callbacks=[lgb.early_stopping(50)] )7.3 时间不够:如何合理分配赛程
华为杯通常给4-5天时间,E题的工作量很大。我的时间分配建议是:第一天完成数据审计和预处理,第二天完成特征工程和基线模型,第三天做模型调优和对比实验,第四天做可视化和论文写作,第五天查漏补缺和格式检查。千万不要前两天都在清洗数据,最后一天才开始写论文,那样肯定来不及。
7.4 论文写作:技术做得好,也要写得好
我见过很多队伍技术做得不错,但论文写得一塌糊涂,最后只拿了成功参赛奖。论文写作要注意:逻辑清晰、图表规范、公式编号、参考文献格式统一。摘要要反复打磨,最好找队友互相审阅。附录里的代码要加注释,不要直接贴一大段没有说明的代码。
8. 关于E题的一些个人体会
做了几届华为杯E题之后,我最大的感受是:这道题考的不是你有多聪明,而是你有多踏实。那些拿一等奖的论文,往往不是用了最前沿的算法,而是把数据清洗做得干干净净、把特征工程做得有理有据、把模型对比做得清清楚楚、把论文写得明明白白。E题没有标准答案,但有一套清晰的评价逻辑:你的分析是否严谨、你的结论是否有数据支撑、你的方案是否可落地。
如果你正在准备2026年的华为杯,我的建议是:提前熟悉Pandas、Scikit-learn、LightGBM这几个工具,找几道历年E题练手,重点练习数据预处理和特征工程。比赛的时候不要贪多,把一个问题做深做透,比每个问题都浅尝辄止要好得多。最后,论文的排版和格式一定要重视,这是评委对你的第一印象。
还有一个很实用的小技巧:比赛期间每天结束时,把当天的工作整理成一个小结,包括做了什么、遇到什么问题、明天计划做什么。这样到了最后写论文的时候,你不需要回忆,直接翻小结就能快速组织内容。这个方法我用了三年,每次都能帮我在最后一天省下至少三个小时。