简介:这份资源是2021年安徽省大数据与人工智能应用竞赛人工智能网络赛本科组的完整赛题数据包,面向高校参赛选手、AI课程学习者及需要真实项目练手的数据科学从业者。内容覆盖两大典型任务:一是基于人脸图像预测年龄标签,二是依据房源信息回归预测房屋价格,房源字段包含电梯情况、楼层、户型、区域、装修情况、面积与建筑时间,且部分信息存在缺失,适合练习缺失值处理与特征工程。资源共1009个文件,以1000张jpg人脸图像、4个csv数据表、3个py脚本及2个txt说明为主,压缩包约13.47MB,训练集、验证集、测试集按17000:3000:3000划分,目录结构清晰。目前已有1068人学习下载,读者可借此复现完整赛题流程,掌握图像分类与回归建模的实战思路。
1. 2021年安徽省大数据与人工智能应用竞赛本科组赛题数据:一份被低估的实战练手素材
如果你正在找一份能同时练大数据处理和人工智能建模的赛题数据,2021年安徽省大数据与人工智能应用竞赛本科组的赛题数据值得认真翻一翻。它不像某些竞赛数据那样只给一个CSV就完事,而是覆盖了数据清洗、特征工程、模型训练到结果提交的完整链路,适合数据科学与大数据技术专业的学生、准备大数据面试题的求职者,以及想用真实赛题练手人工智能大作业的工程师。这份数据的价值不在于难度多高,而在于它的任务边界清晰、评价标准明确,你能在几天内跑通一个完整流程,而不是陷在数据理解的泥潭里。下面我从赛题结构、环境搭建、特征处理、模型调参到避坑,把这条链路拆开讲清楚。
2. 赛题数据长什么样:先搞清楚任务边界再动手
2.1 本科组赛题的典型任务结构
安徽省大数据与人工智能应用竞赛本科组的赛题通常围绕一个具体业务场景展开,比如用户行为预测、文本分类或结构化数据回归。2021年的赛题数据一般包含训练集、测试集和提交示例文件,训练集带标签,测试集不带标签,你需要用训练集训练模型,对测试集预测后按指定格式提交。常见做法是先看提交示例文件的列名和格式,反推任务类型——如果是分类任务,标签列通常是离散值;如果是回归任务,目标列是连续值。这一步看似简单,但很多人直接扎进训练集做EDA,最后发现提交格式对不上,白白浪费时间。
我一般会先写一段脚本把三个文件的基本信息打印出来:行数、列数、每列的数据类型、缺失值比例、标签分布。这样能在五分钟内判断出这是分类还是回归、有没有严重类别不平衡、哪些列需要特殊处理。对于大数据方向的赛题,数据量可能达到几十万行,用pandas直接读没问题,但如果超过内存,就得考虑分块读取或换用更高效的工具。
import pandas as pd # 读取训练集和测试集,先看基本信息 train = pd.read_csv('train.csv') test = pd.read_csv('test.csv') sample_submit = pd.read_csv('sample_submission.csv') print("训练集形状:", train.shape) print("测试集形状:", test.shape) print("提交示例列名:", sample_submit.columns.tolist()) print("\n训练集各列缺失比例:") print(train.isnull().mean().sort_values(ascending=False).head(10)) print("\n标签分布:") print(train['label'].value_counts() if 'label' in train.columns else train.iloc[:, -1].value_counts())这段代码的作用是快速建立对数据的整体认知。train.shape告诉你样本量和特征维度,isnull().mean()帮你定位缺失严重的列,value_counts()让你判断标签是否平衡。如果发现某一类占比不到5%,后续建模就要考虑过采样或调整类别权重。参数方面,head(10)只展示缺失最多的前10列,避免输出刷屏;如果标签列名不是label,需要根据实际列名调整。
2.2 环境准备与依赖版本
赛题数据通常对Python版本和库版本有一定要求,但不会像生产环境那样严格。我建议用Python 3.8以上,pandas 1.3以上,scikit-learn 1.0以上,LightGBM或XGBoost选一个装。如果你打算用深度学习,PyTorch或TensorFlow都可以,但本科组赛题一般用树模型就能拿到不错的名次,没必要上神经网络。安装命令很简单:
pip install pandas numpy scikit-learn lightgbm xgboost matplotlib seaborn如果你在Windows上遇到LightGBM安装失败,常见原因是缺少Visual C++运行库,装一下VC_redist.x64.exe即可。另一个坑是pandas版本太老导致read_csv的low_memory参数行为不一致,建议直接升级到最新稳定版。对于大数据集群部署策略感兴趣的同学,这份赛题数据也可以放到Hive或Spark里做分布式处理,但本科组赛题的数据量通常单机就能搞定,不必过度设计。
3. 从原始数据到特征矩阵:清洗、编码与特征交叉
3.1 缺失值处理与异常值检测
赛题数据里缺失值是常态,关键是怎么处理。数值列可以用中位数或均值填充,类别列可以用众数或单独标记为"缺失"类别。我一般先看缺失比例:低于5%的列直接填充,高于30%的列考虑丢弃或作为特征标记。异常值检测用IQR或Z-score,但要注意赛题数据里的异常值可能是真实业务信号,不要盲目删除。比如用户年龄出现200岁,那肯定是脏数据;但订单金额出现极大值,可能是大客户,删了就丢信息。
import numpy as np # 数值列用中位数填充,类别列用众数填充 num_cols = train.select_dtypes(include=[np.number]).columns.tolist() cat_cols = train.select_dtypes(include=['object']).columns.tolist() for col in num_cols: if train[col].isnull().sum() > 0: median_val = train[col].median() train[col].fillna(median_val, inplace=True) test[col].fillna(median_val, inplace=True) for col in cat_cols: if train[col].isnull().sum() > 0: mode_val = train[col].mode()[0] train[col].fillna(mode_val, inplace=True) test[col].fillna(mode_val, inplace=True) # 用IQR标记异常值,但不删除,而是生成指示特征 for col in num_cols: Q1 = train[col].quantile(0.25) Q3 = train[col].quantile(0.75) IQR = Q3 - Q1 lower = Q1 - 1.5 * IQR upper = Q3 + 1.5 * IQR train[f'{col}_outlier'] = ((train[col] < lower) | (train[col] > upper)).astype(int) test[f'{col}_outlier'] = ((test[col] < lower) | (test[col] > upper)).astype(int)这里的关键决策是用训练集的中位数和众数去填充测试集,而不是各自填充,否则会造成数据泄露。异常值不删除而是生成指示特征,让模型自己决定是否利用这个信息。IQR系数1.5是常规做法,如果数据分布偏态严重,可以改成3.0。注意fillna的inplace参数在新版pandas中可能被弃用,建议写成train[col] = train[col].fillna(median_val)。
3.2 类别编码与特征交叉
类别特征不能直接丢给树模型,需要编码。低基数类别用One-Hot,高基数类别用Target Encoding或Frequency Encoding。我一般对类别数少于10的用One-Hot,多于10的用频率编码,因为Target Encoding容易过拟合,需要配合交叉验证。特征交叉是提分的关键手段,比如把"用户ID"和"商品类别"交叉成一个新特征,或者把数值列分箱后与类别列组合。
from sklearn.preprocessing import LabelEncoder # 低基数类别One-Hot,高基数类别频率编码 low_card_cols = [col for col in cat_cols if train[col].nunique() <= 10] high_card_cols = [col for col in cat_cols if train[col].nunique() > 10] train = pd.get_dummies(train, columns=low_card_cols, drop_first=True) test = pd.get_dummies(test, columns=low_card_cols, drop_first=True) # 确保训练集和测试集列对齐 train, test = train.align(test, join='left', axis=1, fill_value=0) for col in high_card_cols: freq_map = train[col].value_counts(normalize=True).to_dict() train[f'{col}_freq'] = train[col].map(freq_map) test[f'{col}_freq'] = test[col].map(freq_map) test[f'{col}_freq'].fillna(0, inplace=True) # 特征交叉示例:数值列分箱后与类别列组合 train['age_bin'] = pd.cut(train['age'], bins=5, labels=False) train['age_cat_cross'] = train['age_bin'].astype(str) + '_' + train['category'].astype(str) test['age_bin'] = pd.cut(test['age'], bins=5, labels=False) test['age_cat_cross'] = test['age_bin'].astype(str) + '_' + test['category'].astype(str)align函数确保One-Hot后训练集和测试集的列完全一致,避免因为测试集出现新类别导致列数不匹配。频率编码用训练集的分布映射到测试集,测试集出现新类别时填0。特征交叉后可能产生高基数特征,需要再做一次频率编码或Target Encoding。这一步做完,你的特征矩阵基本就准备好了,接下来可以喂给模型。
4. 模型训练与调参:LightGBM为什么是赛题首选
4.1 用LightGBM跑通第一个Baseline
本科组赛题的数据量通常在几万到几十万行,特征维度几十到几百,LightGBM在这种场景下训练速度快、精度高、调参相对容易,是赛题首选。XGBoost也可以,但LightGBM在大数据量下优势更明显。第一个Baseline不需要调参,用默认参数跑一遍,看看交叉验证分数和提交格式是否正确。
import lightgbm as lgb from sklearn.model_selection import StratifiedKFold from sklearn.metrics import accuracy_score, f1_score # 准备特征和标签 feature_cols = [col for col in train.columns if col not in ['label', 'id']] X = train[feature_cols] y = train['label'] X_test = test[feature_cols] # 5折交叉验证 skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) oof_preds = np.zeros(len(X)) test_preds = np.zeros(len(X_test)) for fold, (train_idx, val_idx) in enumerate(skf.split(X, y)): X_train, X_val = X.iloc[train_idx], X.iloc[val_idx] y_train, y_val = y.iloc[train_idx], y.iloc[val_idx] model = lgb.LGBMClassifier( n_estimators=1000, learning_rate=0.05, num_leaves=31, random_state=42, n_jobs=-1 ) model.fit( X_train, y_train, eval_set=[(X_val, y_val)], eval_metric='multi_logloss', callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)] ) oof_preds[val_idx] = model.predict_proba(X_val) test_preds += model.predict_proba(X_test) / skf.n_splits # 评估 oof_labels = np.argmax(oof_preds, axis=1) print("OOF Accuracy:", accuracy_score(y, oof_labels)) print("OOF F1:", f1_score(y, oof_labels, average='weighted'))early_stopping(50)表示验证集指标50轮不提升就停止训练,避免过拟合。n_estimators=1000是上限,实际训练轮数由早停决定。num_leaves=31是默认值,控制树复杂度。learning_rate=0.05比默认的0.1更保守,通常能带来更稳定的验证分数。test_preds累加每折的预测概率再平均,这是标准的Bagging策略。如果任务是回归,把LGBMClassifier换成LGBMRegressor,eval_metric换成rmse,predict_proba换成predict。
4.2 关键参数怎么调:学习率、叶子数和特征采样
LightGBM的参数很多,但真正影响大的就几个:learning_rate、num_leaves、min_child_samples、feature_fraction、bagging_fraction。我一般先用默认参数跑一个Baseline,然后按以下顺序调:先把learning_rate降到0.05,num_leaves提到63,看验证分数是否提升;如果过拟合,降低num_leaves或提高min_child_samples;如果欠拟合,提高num_leaves或降低min_child_samples。feature_fraction和bagging_fraction控制在0.7到0.9之间,能提升泛化能力。
# 调参后的模型配置 model = lgb.LGBMClassifier( n_estimators=2000, learning_rate=0.03, num_leaves=63, min_child_samples=20, feature_fraction=0.8, bagging_fraction=0.8, bagging_freq=5, reg_alpha=0.1, reg_lambda=0.1, random_state=42, n_jobs=-1 )bagging_freq=5表示每5轮做一次Bagging,配合bagging_fraction=0.8使用。reg_alpha和reg_lambda是L1和L2正则化,防止过拟合。如果验证分数波动大,把bagging_freq改成1,bagging_fraction降到0.7。如果训练集很大,min_child_samples可以提到50或100。调参不是越多越好,我一般控制在10组以内,用Optuna或GridSearchCV自动搜,但要注意交叉验证的折数要和最终评估一致。
4.3 模型融合与提交格式检查
单模型跑通后,可以尝试模型融合提分。最简单的是把LightGBM和XGBoost的预测概率加权平均,权重按验证分数分配。更复杂的是Stacking,用第一层模型的输出作为第二层模型的输入,但本科组赛题一般加权平均就够了。提交前一定要检查格式:列名是否和示例文件一致,行数是否和测试集一致,预测值是否在合法范围内。
# 加权融合示例 lgb_preds = test_preds # LightGBM预测概率 xgb_preds = np.load('xgb_test_preds.npy') # XGBoost预测概率 # 按验证分数分配权重,假设LGB分数更高 final_preds = 0.6 * lgb_preds + 0.4 * xgb_preds final_labels = np.argmax(final_preds, axis=1) # 生成提交文件 submission = pd.DataFrame({'id': test['id'], 'label': final_labels}) submission.to_csv('submission.csv', index=False) print("提交文件形状:", submission.shape) print("标签分布:", submission['label'].value_counts())提交前用sample_submission的列名和顺序核对一遍,避免因为列顺序不对导致评分失败。如果任务是回归,np.argmax换成直接输出预测值。标签分布要和训练集大致一致,如果偏差太大,可能是模型有问题或测试集分布不同。
5. 避坑与排查:赛题数据里最容易翻车的五个地方
5.1 数据泄露:用测试集信息填充训练集
现象:交叉验证分数很高,但提交后分数很低。原因:在填充缺失值或编码类别时,用了全量数据(训练集+测试集)的统计量,导致测试集信息泄露到训练集。解决:所有统计量(中位数、众数、频率映射)必须只在训练集上计算,然后应用到测试集。用train[col].median()而不是full_data[col].median()。
5.2 类别编码后列不对齐
现象:训练时特征列有100个,预测时只有95个,报错feature_names mismatch。原因:One-Hot编码后训练集和测试集的类别不一致,测试集缺少某些类别或多了新类别。解决:用align函数对齐,或者用pd.get_dummies后手动补齐缺失列。更稳妥的做法是用sklearn的OneHotEncoder,设置handle_unknown='ignore'。
5.3 早停指标选错导致过拟合
现象:训练集AUC到0.99,验证集AUC只有0.75。原因:早停用的指标和最终评估指标不一致,比如用binary_logloss早停但最终看AUC。解决:早停指标要和赛题评价指标一致。分类任务用multi_logloss或auc,回归任务用rmse或mae。如果不确定赛题用什么指标,看提交示例文件的格式,分类一般看准确率或F1,回归看RMSE。
5.4 提交文件行数或ID顺序错误
现象:提交后系统提示格式错误或分数为0。原因:提交文件的行数和测试集不一致,或者ID列的顺序和测试集不同。解决:生成提交文件时用test['id']而不是重新生成ID,确保行数和顺序完全一致。如果测试集没有ID列,用索引代替,但要在提交示例文件里确认。
5.5 忽略数据分布偏移
现象:训练集和测试集的标签分布差异大,模型在测试集上表现差。原因:赛题数据可能是按时间划分的,训练集是早期数据,测试集是后期数据,分布发生了变化。解决:用时间序列交叉验证代替随机交叉验证,或者在特征中加入时间相关特征。如果发现某个特征在训练集和测试集上的分布差异很大,考虑删除或做归一化。
6. 进阶技巧:用伪标签和特征选择再提两个点
伪标签是赛题后期提分的常用手段:先用训练集训练一个模型,对测试集预测,把置信度高的预测结果作为伪标签加入训练集,重新训练模型。置信度阈值一般设0.9或0.95,太低会引入噪声,太高则伪标签样本太少。我一般会跑两轮伪标签,第一轮用0.9阈值,第二轮用0.95阈值,观察验证分数是否提升。
# 伪标签示例 test_preds_proba = model.predict_proba(X_test) high_conf_idx = np.where(test_preds_proba.max(axis=1) > 0.9)[0] pseudo_labels = test_preds_proba[high_conf_idx].argmax(axis=1) # 把伪标签样本加入训练集 X_pseudo = X_test.iloc[high_conf_idx] y_pseudo = pd.Series(pseudo_labels, index=X_pseudo.index) X_augmented = pd.concat([X, X_pseudo], axis=0) y_augmented = pd.concat([y, y_pseudo], axis=0) # 重新训练 model_pseudo = lgb.LGBMClassifier(**best_params) model_pseudo.fit(X_augmented, y_augmented)特征选择方面,LightGBM自带feature_importances_,可以按重要性排序,保留前50或前100个特征。但要注意,重要性低的特征不一定没用,可能是被其他特征掩盖了。我一般会做两组实验:一组用全部特征,一组用Top 50特征,看验证分数哪个高。如果Top 50更高,说明有噪声特征;如果差不多,就用全部特征,避免丢失信息。
最后说一个我踩过的坑:赛题数据里如果有ID列,千万不要把它当特征喂给模型。ID列通常是唯一标识,和标签没有因果关系,但树模型可能会记住ID和标签的对应关系,导致过拟合。我一般会在特征列表里直接排除ID列,或者用drop删掉。另一个习惯是每次提交前把提交文件用head和describe看一遍,确认没有异常值。这些习惯帮我省了很多后悔药。希望帮到你。
本文还有配套的精品资源,点击获取