简介面向安徽省大数据与人工智能应用竞赛人工智能网络赛本科组选手该资源提供2021年正式赛题全部数据包含人脸年龄标签回归与房屋价格预测两类任务。人脸数据由1000张jpg图片组成对应年龄标注房价预测任务提供train/val/test三个CSV字段涵盖电梯情况、楼层、户型、区域、装修、面积、建筑时间部分信息存在缺失训练集、验证集、测试集按17000:3000:3000划分。包内另有Python脚本和说明文档可辅助数据读取、缺失值检查与基线建模。整个rar压缩包共1009个文件大小13.47MB以图像和表格数据为主轻量易用。目前已有1067人学习适合历年真题备赛、特征工程练习及回归模型实战的本科组选手。1. 赛题数据不是普通数据从 2021 安徽人工智能网络赛说起第一次在竞赛平台上下载这份“2021年安徽省大数据与人工智能应用竞赛人工智能(网络赛)-本科组赛题数据”时我期待的是一套整齐、开箱即用的数据集。实际解压后发现文件名带中文、字段风格不统一、训练集和测试集的行数差距明显。这其实是大数据与人工智能竞赛的常态——主办方给的是贴近真实业务的数据而不是教材里处理干净的样例。本篇文章面向想完整走一遍竞赛流程的本科生和刚入门的从业者按拿到数据后的真实操作顺序讲清楚从读取到提交这套赛题数据的关键路径、参数取舍和最容易翻车的细节。2. 赛题数据目录与读取拿到压缩包后的第一轮体检2.1 解压与信息体检文件布局、编码、体量一起查这类赛题数据通常以压缩包形式发放里面除了若干个 csv一般还带一份说明文档。很多同学拿到后第一件事是双击打开 Excel结果大文件卡顿中文列名乱码特殊字符被隐藏。我一般会在终端里先做一轮体检确认三件事目录里到底有哪些文件、csv 用的是哪种编码、数据规模大概在什么量级。unzip -q contest_data.zip -d data_dir/ find data_dir -maxdepth 3 -type f | sort-q让 unzip 安静解压避免文件列表刷屏-d指定解压目标目录。find按层级列出所有文件一眼就能看出压缩包里除了训练集、测试集之外是否还有额外的说明文档或提交样例。这个步骤不是走形式而是帮你建立对数据来源的预期如果文件命名里带着“v2”“final”这类后缀说明主办方多次调整过数据后续读取时要特别确认版本一致。接着检查文件编码和行数file data_dir/*.csv wc -l data_dir/*.csvfile输出里如果出现UTF-8 (with BOM)或ISO-8859一类标记说明这份数据在生成时经历过不同平台的手工编辑读取时不能所有文件共用一个 encoding 参数。wc -l统计行数可以快速判断数据是几千条还是几十万条这直接决定后面特征工程能不能用 OneHot、需不需要降采样。如果file显示是ASCII text说明这份数据保存得很干净如果显示data或乱码字符就要警惕隐藏分隔符或二进制内容。常见做法是先跑完这三条命令再进 Jupyter否则你会在错误前提下反复试错。2.2 训练集、测试集和提交样例三个文件各做什么以这类人工智能网络赛的惯例压缩包里一般会提供三个核心 csv训练集带目标标签测试集不带标签提交样例只包含 id 和空的目标列。它们的列结构通常不是完全对齐的尤其是测试集可能在特征数量、缺失比例上和训练集有明显差异。先读取并打印结构比直接建模重要得多。import pandas as pd train pd.read_csv(data_dir/train.csv, encodingutf-8-sig) test pd.read_csv(data_dir/test.csv, encodingutf-8-sig) print(train shape:, train.shape) print(test shape:, test.shape) print(train columns:, train.columns.tolist()) print(test columns:, test.columns.tolist())encodingutf-8-sig会主动处理带 BOM 的 UTF-8 文件避免列名首字符出现不可见的\ufeff。如果读出来仍报UnicodeDecodeError说明这份数据实际是 GBK 或 GB2312 编码改成encodinggbk或encodinggb18030再读。这一步不要偷懒编码问题通常只影响部分文件训练集和测试集编码不一致时后续 merge 时会莫名出错。文件作用关键列train.csv训练模型学习特征到目标的映射id、特征列、targettest.csv生成预测模拟真实场景id、特征列sample_submit.csv提交格式参考不参与建模id、目标列空打印shape之后要重点核对 id 列是否唯一。如果 test 里 id 出现重复提交时会因为行数对不上被判无效如果 train 里 id 重复则说明同一行数据可能有多次采样需要去看说明文档确认是否应该按行处理还是按 id 聚合。还有一种情况是测试集包含 train 中的部分 id说明主办方可能做了去重切分这时直接按行 shuffle 会破坏评估逻辑。3. 数据清洗与特征工程把赛题数据变成可训练的矩阵3.1 缺失值、类型错乱与目标变量分布赛题数据最磨人的地方不是模型而是前 40% 的时间都耗在清洗上。先把训练集完整读进来看缺失值分布和目标列的类别占比这一步能避免后续建模阶段被“黑匣子”式的错误带偏。import pandas as pd import numpy as np train pd.read_csv(data_dir/train.csv, encodingutf-8-sig) null_summary train.isnull().sum() null_summary null_summary[null_summary 0].sort_values(ascendingFalse) print(null_summary) if target in train.columns: print(target distribution:) print(train[target].value_counts(normalizeTrue))缺失值统计不要一次性看全表而是筛选大于 0 的列并按缺失量降序排列。这样能立刻识别出两类特征缺失率超过 80% 的直接考虑删除缺失率在 10% 到 50% 的需要判断是随机缺失还是与目标相关的结构化缺失。value_counts(normalizeTrue)输出的比例如果显示某个类别占 90% 以上说明目标严重不平衡模型选择上要放弃准确率这个指标转用 macro F1 或 AUC。还有一个常见操作是把数据类型单独拉出来看train.dtypes里 object 类型的列不代表一定是类别特征有可能是数值列里混入了“?”或“unknown”这类占位符。这时要先统一替换成np.nan再按数值列处理而不是直接做类别编码。3.2 特征工程的三板斧标签编码、标准化与均值编码网络赛的赛题数据一般量级不大特征也不像图像那样可以直接丢给卷积网络。常见做法是先把表格数据变成干净的数值矩阵然后交给树模型。我一般会先做三步类别列标签编码、数值列标准化、高基数类别列做目标均值编码。from sklearn.preprocessing import LabelEncoder, StandardScaler import pandas as pd import numpy as np train pd.read_csv(data_dir/train.csv, encodingutf-8-sig) test pd.read_csv(data_dir/test.csv, encodingutf-8-sig) feature_cols [c for c in train.columns if c not in [id, target]] for col in feature_cols: if train[col].dtype object: le LabelEncoder() train[col] train[col].fillna(MISSING) test[col] test[col].fillna(MISSING) le.fit(pd.concat([train[col], test[col]], axis0)) train[col] le.transform(train[col]) test[col] le.transform(test[col]) else: train[col] train[col].fillna(train[col].median()) test[col] test[col].fillna(test[col].median()) scaler StandardScaler() num_cols train[feature_cols].select_dtypes(include[np.number]).columns.tolist() train[num_cols] scaler.fit_transform(train[num_cols]) test[num_cols] scaler.transform(test[num_cols])这段代码里最关键的是LabelEncoder.fit时用pd.concat([train[col], test[col]])合并两个集合来 fit而不是只 fit 训练集。原因是测试集可能出现训练集里没有的类别单独 fit 会在 transform 时抛ValueError。缺失值填充成字符串MISSING再编码比直接填均值更稳妥树模型能把这个缺失状态当做一个独立的类别分支信息不会丢失。StandardScaler适合逻辑回归、SVM 这类对尺度敏感的模型但如果后面确定用树模型标准化其实可有可无树的切分点在量的绝对大小上不敏感。我一般保留标准化的原因是同一份矩阵可能既要跑树模型也要跑线性模型做对比统一预处理能少改代码。高基数类别列如果直接用 LabelEncoder会引入一个不存在的顺序关系导致树模型学到错误的切分。更稳妥的做法是目标均值编码用训练集中该类别的目标均值替换原始值测试集用全局均值代替。但要注意均值编码容易过拟合需要配合交叉验证使用最好在每一折内部单独计算不要用全量训练集一次算完。4. 模型选型与五折基线网络赛评分规则下的上分路径4.1 评分指标决定模型边界F1、准确率与 AUC 差别在哪安徽省这类人工智能网络赛的评分指标通常会在说明文档里写清楚常见的是准确率、macro F1 或 AUC。这三个指标对应的最优策略完全不同。如果线上用准确率类别不平衡时无脑预测多数类就能拿到高准确率但这不代表模型有效如果线上用 F1尤其是 macro F1每个类别的表现都被平等对待模型就需要在少数类上多下功夫。在做模型选型时我习惯先判断评分方式再决定模型倾向评分指标重点模型倾向accuracy整体正确率多数类优先类别不平衡影响大macro F1各类别平均表现优先保证少数类召回可考虑类别加权AUC排序质量关注概率分数排序不关注绝对阈值logloss概率质量需要概率校准避免过度自信如果明确是分类任务且目标列是二分类最常见的可靠路径是直接用树模型。LightGBM、XGBoost 或者 sklearn 自带的 HistGradientBoostingClassifier 都能处理缺失值也不需要做复杂的特征缩放小样本上收敛快调参空间明显小于深度学习模型。对于本科组赛题数据这种几千到几万行的体量深度学习反而容易过拟合而且训练周期长不适合在线有限时间的网络赛节奏。4.2 五折分层交叉验证一套能直接跑的 baseline拿到清洗好的特征矩阵后我一般先跑五折分层交叉验证而不是直接把数据丢进 fit。这样做的目的有三个一是看模型在不同子集上的稳定性二是为后续概率融合预留 OOF 预测三是避免因为随机切分不均导致单次结果失真。from sklearn.model_selection import StratifiedKFold from sklearn.ensemble import HistGradientBoostingClassifier from sklearn.metrics import f1_score import numpy as np import pandas as pd X feature_df.drop(columns[id, target]) y feature_df[target] skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) oof np.zeros(len(X)) pred np.zeros((len(test_df), 5)) for fold, (tr_idx, va_idx) in enumerate(skf.split(X, y)): X_tr, X_va X.iloc[tr_idx], X.iloc[va_idx] y_tr, y_va y.iloc[tr_idx], y.iloc[va_idx] model HistGradientBoostingClassifier( max_iter300, learning_rate0.1, l2_regularization0.0, random_state42 ) model.fit(X_tr, y_tr) oof[va_idx] model.predict_proba(X_va)[:, 1] pred[:, fold] model.predict_proba(test_df)[:, 1] fold_f1 f1_score(y_va, (oof[va_idx] 0.5).astype(int)) print(ffold {fold}: f1 {fold_f1:.4f}) final_pred pred.mean(axis1) final_f1 f1_score(y, (oof 0.5).astype(int)) print(foverall f1: {final_f1:.4f})StratifiedKFold的分层逻辑是让每一折里目标类别的比例和全量数据保持一致对不平衡数据非常关键。shuffleTrue打乱行顺序配合固定random_state42保证结果可复现。max_iter300决定树的迭代轮数小数据集上 300 轮足够learning_rate0.1是常用的初始学习率调低到 0.05 通常更稳定但需要更多迭代。predict_proba返回的是概率矩阵二分类时取[:, 1]表示正类的概率多分类任务则需把pred定义成(样本数, 类别数, 折数)的三维数组最后在类别维度上做 argmax。OOF 预测是本段代码的核心产出。oof[va_idx]是每一折验证集上的预测概率最终拼成完整的样本外预测用来评估模型真实泛化能力pred[:, fold]是测试集每一折的概率预测取平均后得到更稳定的提交结果。如果你在跑完这段后看到五个 fold 的 F1 波动超过 0.05说明模型对数据切分敏感优先检查特征是否包含噪声或目标是否过于稀疏。5. 避坑清单赛题数据上最容易翻车的五个位置5.1 数据读取层的三个坑路径、编码与行数不一致坑 1中文路径和中文列名导致读取报错。现象pd.read_csv报UnicodeDecodeError或者读入后列名首字符变成\ufeff后续所有按列名操作都匹配不上。原因数据在 Windows 平台打包时被保存为 GBK 编码且列名带 BOM 头Jupyter 工作目录路径包含中文也会让部分旧版本 pandas 报错。解决先运行file data_dir/*.csv查看实际编码encoding参数按文件分开设置路径用纯英文目录不要放在带中文的文件夹下读入后执行train.columns [c.strip() for c in train.columns]清理隐藏字符。坑 2提交文件的行数和测试集对不上。现象平台提交后提示“行数不匹配”本地检查发现pred的样本数比test.csv少了几行。原因清洗阶段用dropna()直接删除含缺失值的行导致测试集行数被缩减或者测试集本身存在重复 id而你在 merge 时默认了一对一关系。解决预测阶段永远保持测试集原有的行数和顺序不删除任何行在生成提交文件前加一行断言assert len(submit) len(test)做最后兜底。坑 3训练集和测试集特征列不对齐。现象训练集有 32 列特征测试集只有 30 列建模时报ValueError: feature names mismatch。原因训练集包含测试集中没有的动态特征列或者在特征工程阶段对训练集做了新增操作但忘了同步到测试集。解决统一以测试集的列集合为基准用reindex对齐并填充缺失feature_cols [c for c in test.columns if c not in [id]] X_train train[feature_cols].copy() X_test test[feature_cols].copy()如果训练集存在但测试集不存在的列不要强行带入模型否则上线时会因为特征缺失而崩溃。5.2 成绩评估层的两个坑本地 CV 与线上分数的落差坑 4本地五折 CV 很高线上分数却明显下降。现象本地f1 0.87线上只有0.79中间没有任何代码改动。原因最常见的是随机种子带来的数据切分运气其次是特征里包含了和 id 相关的隐藏信息例如行号、时间戳模型学到的不是业务规律而是数据录入顺序。解决先换random_state连跑三次如果分数波动超过 0.03说明模型本身不稳定再用对抗验证——把训练集和测试集合并新增一列来源标签训练一个分类器看能否区分两类样本。如果分类 AUC 接近 0.9说明训练集和测试集分布差异极大这类数据上宁可降低模型复杂度也不要盲目堆特征。坑 5线上用 F1 但本地用准确率调阈值。现象本地准确率调优到 0.92提交后线上 F1 只有 0.61比默认阈值还要低。原因准确率偏向多数类把阈值往多数类方向移动后少数类召回率大幅下降F1 被拖垮。解决先确认评分指标如果是 macro F1在本地也使用完全相同的指标计算如果模型输出概率不要在本地暴力搜索阈值而是先把每个类别的召回率单独打印出来观察少数类是否存在整体漏判。6. 提交前的验证技巧概率校准、种子鲁棒与数据漂移检查网络赛提交次数有限每提交一次都在消耗机会成本。我自己的习惯是在真正生成提交文件前做三件事概率校准、种子鲁棒检查和训练测试分布漂移验证。第一件事是概率校准。很多模型输出的概率并不是真实概率而是具备排序意义的分数。如果线上评分是 logloss校准能让分数变好如果线上评分是 F1校准对最终阈值切分的影响很小但仍值得花两分钟画一条校准曲线。from sklearn.calibration import calibration_curve import matplotlib.pyplot as plt curve_x, curve_y calibration_curve(y, oof, n_bins10) plt.plot(curve_x, curve_y, markero, labelmodel) plt.plot([0, 1], [0, 1], --, colorgray, labelideal) plt.xlabel(mean predicted probability) plt.ylabel(fraction of positives) plt.legend() plt.show()n_bins10表示把预测概率按十分位数分箱calibration_curve返回每个箱子的平均预测概率与实际正样本比例。如果曲线明显偏离对角线说明模型过度自信或过度保守可以用sklearn.calibration.CalibratedClassifierCV做等温回归校准但小数据集上校准容易放大噪声我更倾向于直接观察曲线只在曲线呈现明显系统性偏移时才使用。第二件事是种子鲁棒检查。固定random_state跑出的单次提交带有偶然性我一般会跑[42, 2021, 7]三个种子取三次 OOF 分数的中位数作为预期线上分数。如果三次分数差异大于 0.02说明模型不稳定优先考虑减少特征数量或增加正则化而不是继续调参。第三件事是数据漂移检查用训练集和测试集的特征均值做一次简单对比特征均值差异超过 2 个标准差的列可以单独分析是否来自不同的采样时间或采集设备。这套验证流程做完再生成最终的提交文件。以前我总急着把第一次跑出分数就提交结果被线上分数反复打脸后来学会先看校准曲线、先跑种子鲁棒反而省下了大量无意义的提交次数。赛题数据的价值不在那个最终分数而在你能不能把每个环节的误差来源看透。希望帮到你。本文还有配套的精品资源点击获取