3、数据准备:图像数据集收集、数据清洗与标注、数据增强技术、数据加载器构建
数据准备这事儿,说简单也简单,说复杂能让你崩溃三天。我刚开始做风格迁移时,以为模型是核心,后来才发现——数据才是真正的命根子。你喂进去的是垃圾,吐出来的必然是垃圾,这点没得商量。
3.1 图像数据集收集:从哪搞到靠谱的图?
收集数据集,说白了就是给模型找「教材」。教材好不好,直接决定学生水平。
公开数据集是首选。我个人习惯先逛这几个地方:
- Kaggle:啥都有,质量参差不齐,得自己筛
- Hugging Face Datasets:现在越来越全,而且带标注
- Papers with Code:论文附带的数据集,质量通常靠谱
- Google Open Images:900万张图,够你玩很久
但公开数据集有个坑——版权问题。我曾经在项目里用了某个数据集做商业模型,结果对方律师函直接寄到公司。嗯,从那以后我养成了习惯:先看License,再看数据。
如果你需要自己收集,我建议走正规渠道:
- 用Unsplash、Pexels等免费图库的API
- 联系摄影师或机构购买授权
- 自己拍——虽然累,但最干净
3.2 数据清洗与标注:脏数据是魔鬼
数据清洗,我愿称之为「AI炼丹的第一步苦活」。你想想看,100万张图里可能有10%是坏的——模糊的、重复的、格式不对的、标签错的。不清洗?模型直接学歪。
清洗流程我一般这么走:
- 去重:用感知哈希(pHash)算相似度,阈值设0.95以上算重复
- 质量过滤:分辨率低于256x256的、亮度方差过小的、模糊度高的,统统扔掉
- 格式统一:全部转成RGB、统一后缀名(我习惯用.png或.jpg)
- 异常检测:检查损坏文件、全黑图、全白图
标注这块,风格迁移和普通分类不一样。你不需要给每张图打「猫」「狗」标签,而是需要风格标签和内容标签。比如:
| 图片 | 风格标签 | 内容标签 |
|---|---|---|
| 梵高星空 | 后印象派、梵高 | 星空、村庄 |
| 莫奈睡莲 | 印象派、莫奈 | 睡莲、池塘 |
| 浮世绘海浪 | 浮世绘、葛饰北斋 | 海浪、富士山 |
标注工具我推荐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%,就是因为数据加载太慢。
构建加载器的几个关键点:
- 批量大小(batch size):根据显存来定,一般8~64之间。显存不够就梯度累积。
- 多进程加载(num_workers):我习惯设成CPU核心数的一半。设太多反而会卡。
- 预读取(prefetch):提前加载下一批数据,让GPU不空转。
- 内存映射(memory mapping):大数据集用LMDB或HDF5格式,比读单张小图快10倍。
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个点。
- 缓存策略:如果数据集不大(几千张),我建议全部加载到内存里。省去磁盘读取时间,训练速度翻倍。
嗯,数据准备这块就聊到这儿。下一章我们开始讲模型架构——终于到核心部分了。