资讯动态

赛题数据预处理与baseline搭建:从压缩包到可提交结果的关键流程

发布时间:2026/10/17 14:42:43 来源:尧图企业网站定制
简介这是2021年安徽省大数据与人工智能应用竞赛人工智能网络赛本科组的完整赛题数据包面向参加同类竞赛的高校本科生、备赛团队及人工智能课程实训者。数据围绕两项任务一是基于1000张人脸图片及其年龄标签完成年龄估计二是利用电梯情况、楼层、户型、区域、装修情况、面积、建筑时间等房源信息预测房屋价格并提供train、val、test三个CSV文件划分比例约1700030003000便于直接开展训练、验证与测试。压缩包共1009个文件以jpg图像和csv表格为主体另含3个Python脚本和2个txt说明文件整体约13.47MB结构清晰、上手成本低。目前已有1068人学习使用。下载后即可获得规范划分的赛题数据、基础脚本和字段说明既能复现竞赛流程也适合练习特征工程、模型调参与算法对比可作为课程项目或毕业设计的可靠数据支撑。1. 赛题数据不是题库先理解它怎么决定你的排名拿到这份2021年某省大数据与人工智能应用竞赛人工智能网络赛本科组赛题数据很多人的第一反应是赶紧翻代码、找模型、赶第一版提交。但真正能让人拉开差距的往往是打开压缩包后的头三个小时目录结构、标签字段、脏数据这些东西决定你后面所有训练是否白费。赛题数据不是拿来背的题库它是一个带约束条件的工程输入。文件命名、编码、目录层级、标签分布、缺失样本每一项都直接影响模型的训练与最终得分。这篇笔记按我处理类似竞赛数据的一线习惯从检查数据包开始讲到预处理、baseline搭建和最值得注意的四个坑。新手能跟着命令一步步走熟手也能拿这套流程对照自己的项目查漏补缺。2. 解压后先重建目录结构数据包里的文件各是什么角色2.1 解压前摸清数据包三条命令避免解开后懵我一般不会直接双击解压而是先在终端里看一眼压缩包结构。训练集几十万张图片或者一堆嵌套目录时盲解压容易把磁盘撑爆也容易在一堆文件里找不到重点。用命令先读它成本低得多。ls -lh 赛题数据.zip # 看压缩包体积预估是图片还是结构化数据 unzip -l 赛题数据.zip | head -30 # 只查看压缩包内文件清单不解压 du -sh 赛题数据.zip # 再次确认磁盘占用 unzip -q 赛题数据.zip -d data_dir # 静默解压到指定目录前三条命令都不改动文件unzip -l会把包内的路径和文件名列出来配合head -30可以快速判断数据是平铺图片、子目录分类、还是几个 CSV。du -sh看的是整体体积如果压缩包解压后会膨胀很多需要提前留磁盘空间。最后一条的-q表示静默-d data_dir指定解压目标目录我习惯让所有数据都放到单独目录里后续写相对路径遍历时不会被其它文件干扰。解压后如果还看不清层级再跑一句tree -L 2 data_dir看两层就够了。2.2 赛题数据目录的常见骨架训练集、测试集与提交样例这类网络赛的数据包大多遵循同一套约定哪怕具体赛题不同核心角色也就那么几类。我遇到比较多的结构是下面这样的表格。路径或文件角色常见注意点train/训练样本子目录可能是类别也可能是拍摄批次先确认再使用test/需要预测的样本一般没有标签不要花时间去找测试集答案labels.csv训练标签编码、字段名、样本顺序都可能埋雷sample_submission.csv提交格式样例列名、行顺序、小数精度都必须严格对齐如果赛题是图像分类train/和test/下通常是图片文件如果训练集按类别建了子目录那么标签其实是隐含在路径里的可以直接用目录名作为标签不需要再读 CSV。如果train/是一条平铺目录、没有子目录那就需要靠labels.csv把文件名和标签关联起来。结构化数据的赛题则常见train.csv、test.csv和label字段目录层级相对简单但字段类型和缺失值更需要注意。网络赛的数据包通常只给训练标签和测试特征这是比赛的公平边界别指望用某些技巧翻到测试集答案。你所需要的一切验证集都应该从训练集里自己切。2.3 标签文件先读字段别把列名和顺序搞反拿到labels.csv时我建议先只做一件事读出来看前几行看列类型看类别分布。代码量很小但它能挡掉一大批后续问题。import pandas as pd train pd.read_csv(labels.csv) print(train.head(10)) # 先看字段名和前几行内容 print(train.dtypes) # 看列类型有没有把数值读成字符串 print(train[label].value_counts(normalizeTrue)) # 看类别占比初步判断是否均衡这三个输出回答三个问题字段名到底叫什么、数值列有没有被当成字符串、类别样本数是否均衡到值得做特殊处理。dtypes观察object类型往往意味着数字被读成了文本后面排序或映射标签时会出错。value_counts(normalizeTrue)直接显示占比比看绝对数量更直观。如果标签文件是 JSON 而不是 CSV就用json.load打开再print(type(data))先看最外层的结构是列表还是字典再决定怎么转 DataFrame。不要一上来就把 label 转成 0、1、2很多框架需要从字符串标签生成自己的映射表提前转数字反而容易丢原始信息。再做一步检查assert train[id].is_unique确认 id 能唯一标识样本否则后面合并提交时会一对多直接导致零分。3. 预处理赛题数据查泄漏、平衡性与图片质量3.1 查数据泄漏测试集与训练集出现同一张图的判定网络赛时间紧数据包制作过程可能不太严谨训练集和测试集偶尔会出现完全一样的文件。这种情况一旦出现等于测试集答案外泄但反过来如果你误把测试集特征拿去训练线上分数会假性偏高真实应用时直接翻车。先做一层最简单的 MD5 去重检查用哈希集合比对所有图片。import os, hashlib from pathlib import Path def file_md5(path, chunk8192): m hashlib.md5() with open(path, rb) as f: while data : f.read(chunk): m.update(data) return m.hexdigest() train_dir Path(train) test_dir Path(test) exts {.jpg, .jpeg, .png, .bmp} train_hashes {file_md5(p) for p in train_dir.rglob(*) if p.suffix.lower() in exts} dups [p for p in test_dir.rglob(*) if p.suffix.lower() in exts and file_md5(p) in train_hashes] print(测试集与训练集重复文件数:, len(dups))chunk8192表示每次读 8KB避免一次性载入大图把内存打满rglob(*)会递归遍历所有子目录也就是不关心数据是按类别还是按批次组织的。如果重复文件数量不是 0先别急着删除把那些重复路径打印出来核对它们是否同时出现在两个集合里。MD5 只对完全相同的字节有效如果同一张图被重新压缩过MD5 会不同这时需要借助感知哈希或图像尺寸对比做二次确认。竞赛数据包里最常见的还是原样复制MD5 已经足够报警。3.2 统计类别分布决定采样策略前先量化不均衡图像分类赛题经常出现类别不均衡少数类可能只有几十张多数类却有上万张。如果不量化靠肉眼根本看不出来。用 Counter 把各类别数量和占比列出来数据说话。from collections import Counter counter Counter(train[label]) total sum(counter.values()) for cls, cnt in sorted(counter.items(), keylambda x: -x[1]): print(f{cls}: {cnt} ({cnt/total:.2%})) minority min(counter.values()) majority max(counter.values()) print(fmax/min: {majority / minority:.1f}x)当最大类别和最小类别相差 10 倍以上就要认真考虑采样策略了。但决策不能只看比例还要看比赛用的是什么评估指标。如果线上指标是 accuracy训练集本身是从真实分布里抽出来的强行过采样少数类未必能涨点如果指标是 macro F1 或 balanced accuracy那么少数类权重会被放大重采样或加权损失基本是必须的。常见做法是先用类别权重给小类别更多梯度比如sklearn.utils.class_weight.compute_class_weight或 PyTorch 的CrossEntropyLoss(weight...)。不要一看到不均衡就盲目欠采样欠采样会让数据量骤减小模型根本学不够。先把分布打印出来和评估指标一起决定策略这一步能省下很多试错时间。3.3 统一分辨率并检查损坏图片图像赛题最容易翻车的地方图像赛题里模型输入尺寸是固定的而赛题数据里的图片尺寸可能五花八门。最常见翻车方式是直接resize拉伸导致长宽比变形另一种是某张损坏图片在训练中途抛异常整个 epoch 中断。我习惯先做中心裁剪再缩放同时把图片统一转成 RGB。from PIL import Image, ImageFile import numpy as np ImageFile.LOAD_TRUNCATED_IMAGES True # 允许读取部分截断图片但别依赖它 def load_centered(path, size(224, 224)): img Image.open(path).convert(RGB) w, h img.size side min(w, h) left (w - side) // 2 top (h - side) // 2 img img.crop((left, top, left side, top side)) img img.resize(size, Image.Resampling.BILINEAR) return np.asarray(img)convert(RGB)会把 RGBA 四通道图转成三通道避免网络输入维度不一致。中心裁剪取的是宽高中较小的一边裁掉多余的长边再缩放到目标尺寸比直接拉伸更接近主流预训练模型的输入习惯。Image.Resampling.BILINEAR是较新版本 Pillow 的写法旧版本写Image.BILINEAR即可。ImageFile.LOAD_TRUNCATED_IMAGES True只对部分截断图片有效真正损坏的图片照样抛异常所以还是要做一轮损坏检查。bad [] for p in Path(train).rglob(*): if p.suffix.lower() not in {.jpg, .jpeg, .png, .bmp}: continue try: with Image.open(p) as im: im.load() except Exception: bad.append(p) print(损坏图片数量:, len(bad))im.load()会真正解码像素数据只open不load是看不出损坏的。如果bad列表不为空先把这些路径从训练列表里剔除或者用一张纯色占位图替换不要在训练循环里用异常处理去兜底那样排查起来很麻烦。归一化用的均值和标准差可以从这个赛题数据里自己统计也可以直接用预训练模型的推荐值但验证集不要叠加随机增强这是另一个容易被忽略的细节。4. 从赛题数据到第一个可提交结果最小baseline跑通4.1 用分层抽样切出验证集调参前先建立一个可靠标尺赛题数据只给一份训练集测试集没有标签所以必须自己切验证集。如果样本类别不均衡随机切分会造成某些小类别在验证集里缺失于是验证指标忽高忽低完全不可信。用train_test_split加stratify可以按类别比例抽样。from sklearn.model_selection import train_test_split train_df, val_df train_test_split( train, test_size0.2, stratifytrain[label], random_state42 )test_size0.2表示留出 20% 做验证集stratifytrain[label]表示按标签类别分层让训练集和验证集每一类的占比都大致相同。random_state42固定随机种子保证每次重跑划分结果一致。如果赛题数据带有拍摄地点、时间戳或批次字段就不能只做随机划分。同一批次或同一来源的图片容易有背景相近、光照相似的问题随机划分会让模型在验证集里偷看到训练集的环境特征线上分数就会和本地差距很大。这时用GroupKFold按来源分组划分更稳妥。4.2 最小训练线路先用轻量模型打通数据流我一般不会一上来就训练几千轮的 ResNet。先用一个能跑通数据读取、模型前向、loss 反传的最小脚本确认整条链路没有问题再换大模型。这个习惯帮我挡过很多次“代码看着没错训练一启动就崩”的窘境。下面是一个最简单的 PyTorch Dataset 和 DataLoader 写法。import os import torch import numpy as np import torch.nn as nn from torch.utils.data import Dataset, DataLoader from PIL import Image class TrainDS(Dataset): def __init__(self, df, root, size(224, 224), label_mapNone): self.df df.reset_index(dropTrue) self.root root self.size size self.label_map label_map or { lbl: i for i, lbl in enumerate(sorted(df[label].unique())) } def __len__(self): return len(self.df) def __getitem__(self, idx): row self.df.iloc[idx] path os.path.join(self.root, row[path]) img Image.open(path).convert(RGB).resize(self.size) img torch.tensor(np.asarray(img)).permute(2, 0, 1).float() / 255.0 label torch.tensor(self.label_map[row[label]]) return img, label train_loader DataLoader(TrainDS(train_df, data), batch_size32, shuffleTrue, num_workers2)__getitem__返回的是经过缩放的图片张量和整数标签。permute(2, 0, 1)把 Pillow 读出的 HWC 顺序转成 PyTorch 要求的 CHW 顺序。/ 255.0将像素值归一到 0 到 1 区间。batch_size32表示每次取 32 张图shuffleTrue只在训练时使用验证集 DataLoader 必须设置shuffleFalse否则指标会因为顺序不同而无意义。num_workers2用两个子进程加载图片如果环境是 Windowsnum_workers经常触发多进程错误直接改成num_workers0就能避免。这个 Dataset 只做数据流转不要用它判断模型上限先确认 loss 能稳定下降再说。如果不想引入深度学习框架也可以退一步把图片缩放成 64×64 后拉成一维向量直接喂 sklearn 的逻辑回归同样能验证“标签对得上、路径读得到、提交格式没问题”。这套最小链路跑通后你已经胜过了不少还在跟路径斗智斗勇的人。4.3 按样例文件拼提交列名、顺序和概率形式都要对齐提交文件是最容易被低估的一环。模型再强提交格式错位就是零分。我一般不会自己从头构造提交表而是读取官方给的sample_submission.csv保持它的列名和行顺序。sample pd.read_csv(sample_submission.csv) score_dict dict(zip(val_ids, val_scores)) sub pd.DataFrame({ id: sample[id].values, score: [score_dict[x] for x in sample[id].values] }) assert list(sub.columns) list(sample.columns) sub.to_csv(submission.csv, indexFalse)dict(zip(val_ids, val_scores))把每个 id 对应的预测值做成映射表再用官方样例的 id 顺序逐行取值这样即使模型输出的顺序和官方不一致也能自动对齐。assert list(sub.columns) list(sample.columns)确保列名完全一致多一列或少一列都会直接失败。indexFalse避免写出无意义的行号列。如果比赛要求输出的是每个类别的概率而不是单个分值先确认样例里是label字段还是多个概率列如果模型输出的是 logits记得先做 softmax 再提交很多全零分就是这么来的。5. 赛题数据使用中的四个避坑记录5.1 中文标签读出来乱码现象打印labels.csv时中文字段变成乱码标签合并时所有类别都对不上。原因数据文件是从 Excel 导出的常用 GBK 或 GB18030 编码而pandas.read_csv默认按 UTF-8 解析一旦遇到不认识的字节就直接变乱码。解决先用二进制读取再逐个编码尝试而不是靠猜。raw open(labels.csv, rb).read() for enc in [utf-8, gbk, gb18030, latin1]: try: train pd.read_csv(labels.csv, encodingenc) print(成功编码:, enc) break except UnicodeDecodeError: continueutf-8和gbk是两种最可能的编码gb18030是 GBK 的超集能兼容更多生僻字。latin1几乎不会报错但它不是正确解只是兜底方案。乱码问题不解决后续标签映射、分组统计全会受影响这是数据处理里的头号拦路虎。5.2 路径对不上打不开图片现象代码报FileNotFoundError但手动打开文件明明存在。原因压缩包内部有嵌套目录或者项目在 Windows 上生成反斜杠路径而 Linux 环境只认正斜杠也有可能文件名大小写不一致。解决统一用pathlib.Path和相对路径管理不硬编码绝对路径。from pathlib import Path root Path(data) for p in root.rglob(*): rel p.relative_to(root) print(rel.as_posix())rglob(*)递归遍历所有子目录relative_to(root)把绝对路径转换成相对于根目录的路径as_posix()把所有分隔符统一成正斜杠。这样在本地跑完压缩打包到服务器或另一台机器上只要保持root不变路径就不会失效。我在这个坑上吃过血泪亏最冤枉的不是模型不行而是训练到一半才发现一个子目录没被扫描到。5.3 提交即零分现象训练正常验证集指标不错提交后线上分数为 0。原因先检查文件格式。最常见的是输出行顺序和官方样例不一致或者列名被改掉其次是把多分类 logits 直接当概率提交没有做 softmax。解决读取官方样例的行顺序按 id 映射预测结果并在保存前做一致性断言。sample pd.read_csv(sample_submission.csv) pred pd.DataFrame({ id: sample[id].values, score: pred_by_id[sample[id].values] }) assert pred.shape[0] sample.shape[0] assert list(pred.columns) list(sample.columns) assert pred[score].notna().all()assert pred.shape[0] sample.shape[0]保证行数一致notna().all()保证没有缺失值。如果要求的是概率再检查输出范围是否在 0 到 1 之间。零分问题 90% 是格式问题剩下 10% 才是模型问题先按这个顺序排查。5.4 本地验证集和线上成绩脱节现象本地验证集准确率 0.95线上成绩 0.55差距大到离谱。原因随机划分的验证集和官方测试集分布不一致。赛题数据里如果存在同一来源的批次随机划分会让验证集和训练集共享相似样本模型本质上是靠记忆背景或拍摄参数做题换到全新测试集就露馅。解决用分组交叉验证代替随机划分。from sklearn.model_selection import GroupKFold gkf GroupKFold(n_splits5) for fold, (train_idx, val_idx) in enumerate(gkf.split(X, y, groupssample_group)): print(ffold {fold}: train {len(train_idx)}, val {len(val_idx)})GroupKFold的groups参数传入样本的分组标识可以是批次 ID、拍摄时间或来源字段。它保证同一组的所有样本不会同时出现在训练集和验证集这样验证结果才更接近线上。如果赛题数据没有明确的组字段也可以从文件名前缀或目录名推断多花这一步比事后怀疑“玄学”有效得多。6. 调模型前先做两个动作过拟合小样本和提交自检6.1 用 64 张图过拟合验证数据链路不是玄学我拿到赛题数据并切好验证集之后不会直接开训完整模型而是先取 64 张训练样本把模型硬跑过拟合。如果模型在这么小的样本上都没办法把训练 loss 压下去大概率不是网络容量不够而是标签错位、增强太强、或者 Dataset 返回了错误的数据。mini_df train_df.sample(64, random_state42) mini_loader DataLoader(TrainDS(mini_df, data, label_maplabel_map), batch_size8, shuffleTrue, num_workers0)label_map必须和完整训练时保持一致否则后面切回完整训练会重新生成映射导致前几轮标签错乱。小样本过拟合跑通后再换三个不同的random_state切验证集对比指标标准差。如果标准差极大说明验证集量太少或分布不稳定这时不要急着调模型超参数先调整划分策略。这个习惯看似多花半小时实则能省掉好几个下午的无效调参。6.2 提交前做一次空跑自检线上提交次数通常有限不应该拿正式提交去试错。我会在每次提交前把要上传的文件用代码做一轮极值检查。sub pd.read_csv(submission.csv) sample pd.read_csv(sample_submission.csv) assert sub.shape sample.shape assert list(sub.columns) list(sample.columns) assert sub[score].notna().all() assert sub[score].between(0, 1).all()如果输出的是概率between(0, 1)能拦截 logits 直接提交的错误如果输出的是类别标签这个断言就要改成检查是否属于合法类别集合。每次修改模型后都要重新跑一遍别嫌麻烦。格式正确是上线的基础很多翻车不是赛题数据不行而是倒在最后一步。我自己的习惯是拿到任何一份赛题数据先花半天时间把目录、编码、标签分布和划分逻辑全部确认清楚再开始训练。以前我也图快直接开训结果整个下午都在排查路径和乱码提交分数一直是零。后来老老实实走完上面这套流程反而跑得更顺模型选型反而成了最不费脑的一部分。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑