这一篇是在 数据增强 的方向上发力,尝试提升模型的表现。
数据增强的目的是通过人工方式增加训练数据的多样性,从而提高模型的泛化能力,使其能够在未见过的数据上表现得更好。对于图像而言,数据增强包括例如视角、光照、遮挡等情况,使得模型能够学习到更加鲁棒的特征表示。
效果如下:
数据增强的方式有很多:颜色变换、噪声、翻转、仿射变换、自动增强、图片混合 Mixup、图片裁剪 Cutmix 等。
实际上,这些方法并不一定能有效提升训练效果,很有可能产生负面影响;同时,参数的值也需要不断调整。
标记一下,初始代码的分数为 0.53,接下来进行各种尝试。
(1)随机旋转+随机裁剪+颜色抖动
# 定义增强变换
data_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
# 增加的部分
transforms.RandomRotation(30), # 随机旋转角度
transforms.RandomResizedCrop(256, scale=(0.8, 1.0)), # 随机裁剪
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1), # 颜色抖动
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
可以看到,分数小有提升。
(2)调整参数
# 定义增强变换
data_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
transforms.RandomRotation(15), # 随机旋转 ±15 度
transforms.RandomResizedCrop(256, scale=(0.8, 1.0)), # 随机裁剪,裁剪比例 80% 到 100%
transforms.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1, hue=0.05), # 颜色抖动,变化幅度 10% 到 20%
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
可以看到,效果反而下降,说明参数修改的不好。
(3)颜色变换
import torch
from torchvision import transforms
from PIL import ImageFilter
# 自定义模糊变换
class RandomBlur(object):
def __init__(self, radius=1):
self.radius = radius
def __call__(self, img):
return img.filter(ImageFilter.GaussianBlur(self.radius))
# 定义颜色变化增强变换
data_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.Grayscale(num_output_channels=3), # 转换为灰度图像,并扩展到 3 通道
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1), # 颜色抖动
RandomBlur(radius=2), # 应用随机模糊
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
分数比原始还低,说明这种方案效果不好,或者参数选取不对。
(4)调整参数
data_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.Grayscale(num_output_channels=3), # 转换为灰度图像,并扩展到 3 通道
transforms.ColorJitter(
brightness=0.1, # 亮度变化范围 10%
contrast=0.1, # 对比度变化范围 10%
saturation=0.1, # 饱和度变化范围 10%
hue=0.05 # 色调变化范围 5%
),
RandomBlur(radius=1), # 应用模糊,半径为 1
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
分数又有所回升,说明这种方案还是有效的,只不过参数需要调整得当。
(5)自动增强
from torchvision import transforms
# 定义自动增强变换
data_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
transforms.AutoAugment(transforms.AutoAugmentPolicy.CIFAR10), # 使用 CIFAR10 的自动增强策略
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
使用的是 CIFAR10 的自动增强,确实有提升,但是效果真的一般,不如增强变换。
今日达到上限,明天再提交吧。
(6)混合增强,也就是将前面的增强都用上
from torchvision import transforms
# 自定义模糊变换
class RandomBlur(object):
def __init__(self, radius=1):
self.radius = radius
def __call__(self, img):
return img.filter(ImageFilter.GaussianBlur(self.radius))
# AugMix 数据增强
class AugMix(object):
def __init__(self, alpha=1.0, num_ops=3):
self.alpha = alpha
self.num_ops = num_ops
self.operations = [
transforms.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1, hue=0.05),
transforms.RandomRotation(degrees=30),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
transforms.RandomAffine(degrees=0, translate=(0.1, 0.1)),
transforms.RandomResizedCrop(size=256, scale=(0.8, 1.0)),
RandomBlur(radius=1)
]
def __call__(self, img):
img = Image.fromarray(np.array(img))
mixed_img = img.copy()
for _ in range(self.num_ops):
op = np.random.choice(self.operations)
augmented_img = op(img)
if np.random.rand() < 0.5:
mixed_img = Image.blend(mixed_img, augmented_img, alpha=self.alpha)
else:
mixed_img = Image.blend(mixed_img, augmented_img, alpha=1 - self.alpha)
return mixed_img
# 定义变换和 AugMix 增强
data_transforms = transforms.Compose([
transforms.Resize((256, 256)),
AugMix(alpha=0.5, num_ops=3),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
(7)使用 Mixup,就是将两个不同的图像及其标签按照一定的比例混合,从而创建一个新的训练样本
from torchvision import transforms
# 定义 MixUp 数据增强
class MixUp(object):
def __init__(self, alpha=0.4):
self.alpha = alpha
def __call__(self, img1, img2, label1, label2):
# 混合比例
lambda_ = np.random.beta(self.alpha, self.alpha)
# 图像混合
mixed_img = lambda_ * img1 + (1 - lambda_) * img2
# 标签混合
mixed_label = lambda_ * label1 + (1 - lambda_) * label2
return mixed_img, mixed_label
# 自定义数据加载器,应用 MixUp
class MixUpDataLoader(DataLoader):
def __init__(self, dataset, batch_size=32, shuffle=True, num_workers=4, pin_memory=True, alpha=0.4):
super().__init__(dataset, batch_size=batch_size, shuffle=shuffle, num_workers=num_workers, pin_memory=pin_memory)
self.mixup = MixUp(alpha=alpha)
def __iter__(self):
for batch in super().__iter__():
images, targets = batch
# 如果batch中的图像数量大于1
if len(images) > 1:
batch_size = len(images)
mixed_images = torch.zeros_like(images)
mixed_targets = torch.zeros_like(targets)
for i in range(batch_size):
img1, label1 = images[i], targets[i]
img2, label2 = images[(i + 1) % batch_size], targets[(i + 1) % batch_size]
mixed_img, mixed_label = self.mixup(img1, img2, label1, label2)
mixed_images[i] = mixed_img
mixed_targets[i] = mixed_label
yield mixed_images, mixed_targets
else:
yield images, targets
# 定义变换
data_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
(8)使用 Cutmix,就是将一个图像的一部分剪切并粘贴到另一个图像上来创建新的训练样本
import torch
import numpy as np
from torchvision import transforms
# 定义 CutMix 数据增强
class CutMix(object):
def __init__(self, alpha=1.0):
self.alpha = alpha
def __call__(self, img1, img2, label1, label2):
# 混合比例
lambda_ = np.random.beta(self.alpha, self.alpha)
# 图像大小
w, h = img1.size
# 随机选择裁剪区域的大小和位置
cutout_width = int(w * np.sqrt(1 - lambda_))
cutout_height = int(h * np.sqrt(1 - lambda_))
x = np.random.randint(0, w - cutout_width)
y = np.random.randint(0, h - cutout_height)
# 创建裁剪区域
cutout = img2.crop((x, y, x + cutout_width, y + cutout_height))
# 将裁剪区域应用到 img1
mixed_img = img1.copy()
mixed_img.paste(cutout, (x, y))
# 标签混合
mixed_label = lambda_ * label1 + (1 - lambda_) * label2
return mixed_img, mixed_label
# 自定义数据加载器,应用 CutMix
class CutMixDataLoader(DataLoader):
def __init__(self, dataset, batch_size=32, shuffle=True, num_workers=4, pin_memory=True, alpha=1.0):
super().__init__(dataset, batch_size=batch_size, shuffle=shuffle, num_workers=num_workers, pin_memory=pin_memory)
self.cutmix = CutMix(alpha=alpha)
def __iter__(self):
for batch in super().__iter__():
images, targets = batch
# 如果batch中的图像数量大于1
if len(images) > 1:
batch_size = len(images)
mixed_images = torch.zeros_like(images)
mixed_targets = torch.zeros_like(targets)
for i in range(batch_size):
img1, label1 = images[i], targets[i]
img2, label2 = images[(i + 1) % batch_size], targets[(i + 1) % batch_size]
mixed_img, mixed_label = self.cutmix(img1, img2, label1, label2)
mixed_images[i] = transforms.ToTensor()(mixed_img)
mixed_targets[i] = mixed_label
yield mixed_images, mixed_targets
else:
yield images, targets
# 定义变换
data_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
代码中已附加注释,不再赘述。
很容易发现,这种增强带来的不一定是正向收益,而且也不是混合得越多越好,这需要我们不断地去尝试,去试错。这篇文章就没有介绍不同的数据增强代表的含义,是因为确实简单,看看开头的那张图就能很容易理解。
其他优化方案比如改进模型,调整训练轮数等等都在上一篇文章中,本篇就是介绍实践过程与结果。
感谢九月大佬的直播讲解,以及源码展示:https://www.kaggle.com/code/chg0901/deepfake-ffdi-ch3-modified-by-hong
上一篇链接:Datawhale AI 夏令营——CV图像竞赛(Deepfake攻防)——Task2学习笔记-CSDN博客
标签:__,img,AI,self,Datawhale,transforms,mixed,alpha,CV From: https://blog.csdn.net/m0_63566347/article/details/140574840