3、数据准备:图像数据集收集、数据清洗与标注、数据增强技术、数据加载器构建

数据准备这事儿,说简单也简单,说复杂能让你崩溃三天。我刚开始做风格迁移时,以为模型是核心,后来才发现——数据才是真正的命根子。你喂进去的是垃圾,吐出来的必然是垃圾,这点没得商量。

3.1 图像数据集收集:从哪搞到靠谱的图?

收集数据集,说白了就是给模型找「教材」。教材好不好,直接决定学生水平。

公开数据集是首选。我个人习惯先逛这几个地方:

  • Kaggle:啥都有,质量参差不齐,得自己筛
  • Hugging Face Datasets:现在越来越全,而且带标注
  • Papers with Code:论文附带的数据集,质量通常靠谱
  • Google Open Images:900万张图,够你玩很久

但公开数据集有个坑——版权问题。我曾经在项目里用了某个数据集做商业模型,结果对方律师函直接寄到公司。嗯,从那以后我养成了习惯:先看License,再看数据。

⚠️ 避坑指南: 别用爬虫随便扒图!我曾经图省事,写了个爬虫去某图库扒了2万张图,结果训练出来的模型有严重的水印残留。更惨的是,那批图还有大量重复和低质量样本,清洗花了我整整一周。

如果你需要自己收集,我建议走正规渠道:

  • Unsplash、Pexels等免费图库的API
  • 联系摄影师或机构购买授权
  • 自己拍——虽然累,但最干净

3.2 数据清洗与标注:脏数据是魔鬼

数据清洗,我愿称之为「AI炼丹的第一步苦活」。你想想看,100万张图里可能有10%是坏的——模糊的、重复的、格式不对的、标签错的。不清洗?模型直接学歪。

清洗流程我一般这么走:

  1. 去重:用感知哈希(pHash)算相似度,阈值设0.95以上算重复
  2. 质量过滤:分辨率低于256x256的、亮度方差过小的、模糊度高的,统统扔掉
  3. 格式统一:全部转成RGB、统一后缀名(我习惯用.png或.jpg)
  4. 异常检测:检查损坏文件、全黑图、全白图
💡 我的小技巧: 写个脚本先跑一遍统计信息——图片数量、尺寸分布、通道数、文件大小。看到异常值,基本就能定位问题。比如文件大小只有1KB的,十有八九是坏图。

标注这块,风格迁移和普通分类不一样。你不需要给每张图打「猫」「狗」标签,而是需要风格标签内容标签。比如:

图片风格标签内容标签
梵高星空后印象派、梵高星空、村庄
莫奈睡莲印象派、莫奈睡莲、池塘
浮世绘海浪浮世绘、葛饰北斋海浪、富士山

标注工具我推荐Label Studio,开源免费,支持自定义标签体系。记得,标注一致性比标注精度更重要——两个人标同一张图,风格标签得一样。

3.3 数据增强技术:让有限的数据变无限

数据增强,说白了就是「一张图变出十张图」。为什么要做?因为模型需要见过各种情况,才能泛化得好。

我常用的增强方法分两类:

基础几何变换:

  • 随机旋转(±15度以内,别转太多)
  • 水平翻转(风格迁移里常用,但注意文字类不能翻)
  • 随机裁剪(保持内容主体完整)
  • 缩放(0.8~1.2倍)

颜色与纹理变换:

  • 亮度/对比度调整
  • HSV抖动(色调、饱和度、明度微调)
  • 高斯模糊(模拟失焦)
  • 随机擦除(强迫模型关注全局)
🎯 实战经验: 风格迁移任务中,颜色增强要谨慎。你想想看,如果训练时把梵高画的蓝色天空增强成了红色,模型可能就学歪了——它以为梵高的风格里包含「红色天空」。我一般只做几何变换和轻微的颜色抖动。

代码示例(用PyTorch的transforms):

from torchvision import transforms

train_transform = transforms.Compose([
    transforms.Resize((256, 256)),
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.RandomRotation(degrees=10),
    transforms.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

3.4 数据加载器构建:别让GPU闲着

数据加载器,就是模型和硬盘之间的「传送带」。传送带慢了,GPU就得干等着——这叫I/O瓶颈。我见过有人用RTX 3090训练,结果GPU利用率只有30%,就是因为数据加载太慢。

构建加载器的几个关键点:

  1. 批量大小(batch size):根据显存来定,一般8~64之间。显存不够就梯度累积。
  2. 多进程加载(num_workers):我习惯设成CPU核心数的一半。设太多反而会卡。
  3. 预读取(prefetch):提前加载下一批数据,让GPU不空转。
  4. 内存映射(memory mapping):大数据集用LMDB或HDF5格式,比读单张小图快10倍。
⚠️ 注意: num_workers不是越大越好。我曾经在32核服务器上设了24个worker,结果系统直接卡死——磁盘I/O成了瓶颈。后来改成8个,反而跑得更快。

PyTorch的标准做法:

from torch.utils.data import DataLoader, Dataset

class StyleTransferDataset(Dataset):
    def __init__(self, content_dir, style_dir, transform=None):
        self.content_paths = glob.glob(f"{content_dir}/*.jpg")
        self.style_paths = glob.glob(f"{style_dir}/*.jpg")
        self.transform = transform
    
    def __len__(self):
        return min(len(self.content_paths), len(self.style_paths))
    
    def __getitem__(self, idx):
        content_img = Image.open(self.content_paths[idx])
        style_img = Image.open(self.style_paths[idx % len(self.style_paths)])
        
        if self.transform:
            content_img = self.transform(content_img)
            style_img = self.transform(style_img)
        
        return content_img, style_img

# 构建加载器
dataloader = DataLoader(
    dataset,
    batch_size=16,
    shuffle=True,
    num_workers=4,
    pin_memory=True,  # 加速数据传输到GPU
    drop_last=True    # 扔掉最后一个不完整的batch
)

这里有个细节——pin_memory=True。这行代码能让数据从CPU传到GPU的速度快不少。我刚开始做的时候没加这行,训练速度慢了将近20%。

3.5 实战中的坑与经验

最后聊几个我踩过的坑:

  • 数据不平衡:某个风格类别只有10张图,另一个有1000张。模型会偏向学多的那个。解决办法——对少的类别做更多增强,或者用加权采样。
  • 标签噪声:标注人员把「印象派」标成了「后印象派」。这种错误多了,模型就混乱了。我后来加了二次审核流程,准确率直接提升5个点。
  • 缓存策略:如果数据集不大(几千张),我建议全部加载到内存里。省去磁盘读取时间,训练速度翻倍。
📌 总结一句话: 数据准备花的时间,会在模型训练阶段加倍还给你。别急着跑模型,先把数据搞干净。

嗯,数据准备这块就聊到这儿。下一章我们开始讲模型架构——终于到核心部分了。