别再为小样本发愁了!手把手教你下载和配置CUB-200-2011、Omniglot等5个经典Few-shot Learning数据集
小样本学习实战:五大经典数据集配置指南与避坑手册
当你在深夜调试完最后一个参数,准备复现那篇惊艳的小样本学习论文时,突然发现第一个障碍不是模型架构,而是——数据集该怎么处理?这份指南将带你用工程师的视角,解剖CUB-200-2011、Omniglot等五个关键数据集的"骨骼结构"。
1. 环境准备与工具链搭建
在开始下载数据集前,建议先建立标准化的工作环境。我习惯使用conda创建独立环境:
conda create -n fewshot python=3.8 conda activate fewshot pip install torch torchvision pandas jupyter对于图像处理,建议准备以下工具包:
- OpenCV:处理不规则尺寸图像
- Pillow:基础图像操作
- tqdm:下载进度监控
注意:所有数据集操作建议在SSD硬盘上进行,特别是Omniglot的数万个小文件处理,HDD可能导致解压时间翻倍
2. CUB-200-2011:鸟类细粒度分类实战
这个包含200种鸟类的数据集看似简单,却暗藏三个技术深坑:
2.1 下载与目录重构
官方压缩包解压后会得到混乱的目录结构,建议用以下脚本重组:
import os from shutil import copyfile def reorganize_cub(root_path): with open(os.path.join(root_path, 'images.txt')) as f: img_paths = [line.strip().split()[1] for line in f] for src in img_paths: dst = os.path.join(root_path, 'images_reorg', src) os.makedirs(os.path.dirname(dst), exist_ok=True) copyfile(os.path.join(root_path, 'images', src), dst)2.2 标注文件解析技巧
关键标注文件处理示例:
import pandas as pd def load_annotations(root_path): bbox = pd.read_csv(os.path.join(root_path, 'bounding_boxes.txt'), sep=' ', header=None, names=['id','x','y','w','h']) split = pd.read_csv(os.path.join(root_path, 'train_test_split.txt'), sep=' ', header=None, names=['id','is_train']) return bbox.merge(split, on='id')2.3 数据加载器优化方案
使用内存映射技术加速加载:
from torch.utils.data import Dataset import numpy as np class CUBDataset(Dataset): def __init__(self, root): self.image_paths = [...] # 初始化路径列表 self.memmap = np.memmap('cub_cache.dat', dtype='uint8', mode='r', shape=(len(self.image_paths), 224, 224, 3)) def __getitem__(self, idx): return self.memmap[idx]3. Omniglot:多语言字符处理的特殊挑战
这个包含50种文字的数据集需要特别注意:
3.1 笔画数据预处理
原始笔画数据需要标准化处理:
def process_stroke(stroke_path): with open(stroke_path) as f: points = [] for line in f: if line.startswith(('START','BREAK')): continue x, y, t = map(float, line.strip().split(',')) points.append([x, y, t]) return np.array(points).T # 转置为(3, N)格式3.2 高效数据加载方案
使用HDF5优化小文件读取:
import h5py def convert_to_hdf5(src_folder, dst_file): with h5py.File(dst_file, 'w') as hf: for char_dir in glob.glob(f"{src_folder}/*/*"): char_name = os.path.basename(char_dir) grp = hf.create_group(char_name) for i, img_path in enumerate(glob.glob(f"{char_dir}/*.png")): img = Image.open(img_path) grp.create_dataset(f"img_{i}", data=np.array(img))4. mini-ImageNet与tiered-ImageNet:版本控制策略
这两个ImageNet子集存在多个版本陷阱:
4.1 版本对比表
| 特征 | Vinyals版本 | Ravi版本 | 第三方版本 |
|---|---|---|---|
| 类别划分 | 64:16:20 | 64:16:20 | 随机划分 |
| 图片尺寸 | 不一致 | 不一致 | 统一84x84 |
| 标注格式 | 无 | CSV | JSON |
4.2 数据验证脚本
def validate_miniimagenet(data_dir, csv_path): df = pd.read_csv(csv_path) missing = [] for _, row in df.iterrows(): if not os.path.exists(os.path.join(data_dir, row['filename'])): missing.append(row['filename']) print(f"缺失文件比例:{len(missing)/len(df):.2%}")5. CIFAR衍生数据集:内存优化方案
CIFAR-FS和FC100的特殊处理技巧:
5.1 内存映射加载器
class CIFARFewShot(Dataset): def __init__(self, root): self.data = np.memmap(os.path.join(root, 'data.bin'), dtype='uint8', mode='r', shape=(60000, 32, 32, 3)) self.labels = np.load(os.path.join(root, 'labels.npy')) def __getitem__(self, idx): return self.data[idx], self.labels[idx]5.2 超类别处理工具
def get_superclass_mapping(): return { 'aquatic_mammals': ['beaver', 'dolphin',...], 'fish': ['aquarium_fish', 'flatfish',...], # ...其他超类别 }6. 跨数据集统一接口设计
建议实现通用数据加载接口:
class FewShotDataset: @staticmethod def from_name(name, **kwargs): if name == 'cub': return CUBDataset(**kwargs) elif name == 'omniglot': return OmniglotDataset(**kwargs) # 其他数据集... def get_episode(self, n_way, k_shot): """实现统一的episode生成接口""" pass在最后测试阶段发现,Omniglot的笔画数据时间戳需要归一化处理才能获得最佳效果,这个细节在原始论文中很少提及。建议在实际加载时添加时间维度的标准化层。
