2022-天池-AI Challengers 赛后记录 264/3691

1.前言

这是自己第一次参加的CV方向的比赛,是一种对抗性攻击的比赛,最后没有进决赛,成绩是90,264/3691,前50名98+的分数,卷不过,不过第一次,之后继续加油了。
这篇文章还学习了前排大佬的思路,尝试跑了一下,当作学习。

比赛地址:https://tianchi.aliyun.com/competition/entrance/531939/introduction
代码地址:https://github.com/Fanthers/data_science_competition/tree/main/%E5%A4%A9%E6%B1%A0/2022%20AI%20Challengers
比赛要求和数据:
官方提供了ResNet50和DensNet121标准网络(基于Pytorch),选手需要在线下基于CIFAR-10数据进行模型训练,再将两个模型提交到线上进行评测(线上的测试数据是不可见的,但是官方有说包含了部分CIFAR-10的测试集,所以不建议大家在线下使用CIFAR-10测试集进行训练),但是看前排的分数,大家普遍使用了测试集用于训练,如果不用很难进复赛,所以初赛不得不用!

2.比赛思路

官方baseline:官方baseline使用CIFAR-10的1w张测试集用于训练,直接提交到线上评测,分数在75左右!(可能我算力不行或者参数问题,最后只有70)
标签平滑技术+基于Albumentations库的数据增强。主要是高斯噪声,随机噪声,颜色,几何变换等,将1w张原始数据扩充到5w张,进行线上的评测,分数已经可以来到90+!
使用FGSM[3]生成5w对抗样本 ,线上分数可以来到0.94+!
Albumentations 2w+FGSM3w 训练样本 0.96+!
后面两种自己也尝试跑通了,但是时间已过,无法提交查看成绩,很遗憾,分数是前排大佬的结果,可能自己提交,因为算力等方面会有稍许降低。下面给出一些重要的源代码,具体代码放在github地址了。

3.关键技术

标签平滑(Label Smoothing):
具体学习参考https://cloud.tencent.com/developer/article/1815786

confidence = 0.8
smoothing = 0.2
cls= 10
dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=False)
images = []
soft_labels = []
transform=get_train_transforms()
cnt=-1
for image, label in tqdm(dataset):
    image = np.array(image)
    #images.append(image)
    soft_label = np.zeros(10)
    soft_label[label] += confidence # an unnormalized soft label vector
    for inx in range(soft_label.shape[0]):
        if inx!=label:
            soft_label[inx]=smoothing / (cls - 1)

基于Albumentations库的数据增强:
具体学习可参考https://zhuanlan.zhihu.com/p/107399127

import albumentations as A
def get_train_transforms():
    return Compose(
        [
            Transpose(p=0.25),
            GaussNoise(p=0.75),
            OneOf([
                    # 模糊相关操作
                    MotionBlur(p=.75),
                    MedianBlur(blur_limit=3, p=0.5),
                    Blur(blur_limit=3, p=0.75),
                ], p=0.25),
            ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.2, rotate_limit=45, p=0.25),
            OneOf([
                # 畸变相关操作
                OpticalDistortion(p=0.75),
                GridDistortion(p=0.25),
                PiecewiseAffine(p=0.75),
            ], p=0.25),
            OneOf([
                    # 锐化、浮雕等操作
                    CLAHE(clip_limit=2),
                    Sharpen(),
                    Emboss(),
                    RandomBrightnessContrast(),
                ], p=0.25),
            #
            HueSaturationValue(hue_shift_limit=0.2, sat_shift_limit=0.2, val_shift_limit=0.2, p=0.5),
            OneOf(
                [
                CoarseDropout(max_holes=4,
                            max_height=4,
                            max_width=4,
                            p=0.5),
                Cutout(
                    num_holes=4,
                    max_h_size=4,
                    max_w_size=4,
                    p=0.5,)],
                p=0.5)
        ]
        )

当然了,直接使用高斯噪声和椒盐噪声也可以达到90的分数

class AddSaltPepprerNoise(object):
    def __init__(self, density=0):
        self.density = density

    def __call__(self, img):
        img = np.array(img)
        h, w, c = img.shape
        Nd = self.density
        Sd = 1 - Nd
        mask = np.random.choice((0, 1, 2), size=(h, w, 1), p=[Nd/2.0, Nd/2.0, Sd])
        mask = np.repeat(mask, c, axis=2)
        img[mask==0] = 0
        img[mask == 1] = 255
        img = Image.fromarray(img.astype('uint8')).convert('RGB')
        return img


class AddGaussianNoise(object):
    def __init__(self, mean=0.0, variance=1.0, amplitude=20.0):
        self.mean = mean
        self.variance = variance
        self.amplitude = amplitude
    def __call__(self, img):
        img = np.array(img)
        h, w, c = img.shape
        N = self.amplitude * np.random.normal(loc=self.mean, scale=self.variance, size=(h, w, 1))
        N = np.repeat(N, c, axis=2)
        img = N + img
        img[img > 255] = 255
        img = Image.fromarray(img.astype('uint8')).convert('RGB')
        return img

my_transform_gaussian_noise = AddGaussianNoise()
my_transform_saltpepper_noise = AddSaltPepprerNoise()

trans = torchvision.transforms.RandomChoice([my_transform_saltpepper_noise, my_transform_gaussian_noise])

FGSM:
具体学习可参考https://pytorch.org/tutorials/beginner/fgsm_tutorial.html#fast-gradient-sign-attack
核心代码:

def fgsm_attack(image, epsilon, data_grad):
    # 收集数据梯度的元素符号
    sign_data_grad = data_grad.sign()
    # 通过调整输入图像的每个像素来创建扰动图像
    perturbed_image = image + epsilon * sign_data_grad
    # 添加剪切以维持[0,1]范围,对于标准化的情况不适用,先注释掉
    # perturbed_image = torch.clamp(perturbed_image, 0, 1)
    # 返回被扰动的图像
    return perturbed_image

##########

def test(trainloader, model, epsilon):
    accs = AverageMeter()
    accs_adv = AverageMeter()
    model.eval()
    # print(len(trainloader))
    for (inputs, soft_labels) in trainloader:
        inputs, soft_labels = inputs.cuda(), soft_labels.cuda()
        inputs.requires_grad = True  # 获取输入梯度
        targets = soft_labels.argmax(dim=1)
        outputs = model(inputs)
        #
        loss = cross_entropy(outputs, soft_labels)
        acc = accuracy(outputs, targets)
        accs.update(acc[0].item(), inputs.size(0))
        # Zero all existing gradients
        model.zero_grad()
        #
        loss.backward()

        # Collect datagrad
        data_grad = inputs.grad.data

        # Call FGSM Attack
        perturbed_inputs = fgsm_attack(inputs, epsilon, data_grad)
        outputs = model(perturbed_inputs)
        acc = accuracy(outputs, targets)
        accs_adv.update(acc[0].item(), inputs.size(0))
        # --
        save_adv_sample(perturbed_inputs, soft_labels)
        # break
    return accs.avg, accs_adv.avg
posted @ 2022-01-18 13:26  小Aer  阅读(7)  评论(0编辑  收藏  举报  来源