全球Deepfake攻防挑战赛:三等奖 Team VisionRush 方案与代码解析

学术   2024-09-25 15:05   北京  
  • 项目名称:DeepFake Defenders
  • 团队:Team VisionRush
  • 机构:中国科学院自动化研究所

https://github.com/VisionRush/DeepFakeDefenders

unsetunset模块1:训练流程unsetunset

https://github.com/VisionRush/DeepFakeDefenders/blob/main/main_train.py

  • 定义数据集路径和网络参数,如类别数量、输入尺寸等。
  • 配置优化器和学习率调度器的参数,这些参数用于调整学习过程中的学习率。
  • 配置分布式数据并行环境,准备多GPU训练。
  • 定义数据转换流程,并加载训练和验证数据集。
  • 进行多个周期的训练和验证,使用EMA技术提高模型性能。

unsetunset模块2:分类模型介绍unsetunset

ConvNeXt

https://arxiv.org/pdf/2201.03545

ConvNeXt是一种新型的卷积神经网络(ConvNet)架构,由Facebook AI Research (FAIR) 和加州大学伯克利分校的研究人员开发。

ConvNeXt完全基于传统的卷积神经网络构建,不包含任何Transformer中的自注意力机制。在ImageNet分类任务上达到了87.8%的top-1准确率,超过了Swin Transformer等当时的最先进模型。

在相似的计算复杂度下,ConvNeXt在多个基准测试中达到了与Transformer相媲美或更好的性能。在高分辨率输入的情况下,ConvNeXt显示出比Swin Transformer更高的推理吞吐量。

ConvNeXt 基本构建块

使用深度可分离卷积(dwconv),后面跟着层归一化(LayerNorm),然后是两个1x1的卷积(用线性层实现),中间是GELU激活函数。

class Block(nn.Module):
    def __init__(self, dim, drop_path=0., layer_scale_init_value=1e-6):
        super().__init__()
        self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim) # depthwise conv
        self.norm = LayerNorm(dim, eps=1e-6)
        self.pwconv1 = nn.Linear(dim, 4 * dim) # pointwise/1x1 convs, implemented with linear layers
        self.act = nn.GELU()
        self.pwconv2 = nn.Linear(4 * dim, dim)
        self.gamma = nn.Parameter(layer_scale_init_value * torch.ones((dim)), 
                                    requires_grad=Trueif layer_scale_init_value > 0 else None
        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()

    def forward(self, x):
        input = x
        x = self.dwconv(x)
        x = x.permute(0231# (N, C, H, W) -> (N, H, W, C)
        x = self.norm(x)
        x = self.pwconv1(x)
        x = self.act(x)
        x = self.pwconv2(x)
        if self.gamma is not None:
            x = self.gamma * x
        x = x.permute(0312# (N, H, W, C) -> (N, C, H, W)

        x = input + self.drop_path(x)
        return x

ConvNeXt 模型

由一个下采样层序列(downsample_layers)和四个不同分辨率阶段的特征提取层(stages)组成。

class ConvNeXt(nn.Module):
    def __init__(self, in_chans=3, num_classes=1000
                 depths=[3393], dims=[96192384768], drop_path_rate=0.
                 layer_scale_init_value=1e-6, head_init_scale=1.,
                 )
:

        super().__init__()

        self.downsample_layers = nn.ModuleList() # stem and 3 intermediate downsampling conv layers
        stem = nn.Sequential(
            nn.Conv2d(in_chans, dims[0], kernel_size=4, stride=4),
            LayerNorm(dims[0], eps=1e-6, data_format="channels_first")
        )
        self.downsample_layers.append(stem)
        for i in range(3):
            downsample_layer = nn.Sequential(
                    LayerNorm(dims[i], eps=1e-6, data_format="channels_first"),
                    nn.Conv2d(dims[i], dims[i+1], kernel_size=2, stride=2),
            )
            self.downsample_layers.append(downsample_layer)

        self.stages = nn.ModuleList() # 4 feature resolution stages, each consisting of multiple residual blocks
        dp_rates=[x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] 
        cur = 0
        for i in range(4):
            stage = nn.Sequential(
                *[Block(dim=dims[i], drop_path=dp_rates[cur + j], 
                layer_scale_init_value=layer_scale_init_value) for j in range(depths[i])]
            )
            self.stages.append(stage)
            cur += depths[i]

        self.norm = nn.LayerNorm(dims[-1], eps=1e-6# final norm layer
        self.head = nn.Linear(dims[-1], num_classes)

        self.apply(self._init_weights)
        self.head.weight.data.mul_(head_init_scale)
        self.head.bias.data.mul_(head_init_scale)

    def _init_weights(self, m):
        if isinstance(m, (nn.Conv2d, nn.Linear)):
            trunc_normal_(m.weight, std=.02)
            nn.init.constant_(m.bias, 0)

    def forward_features(self, x):
        for i in range(4):
            x = self.downsample_layers[i](x)
            x = self.stages[i](x)
        return self.norm(x.mean([-2-1])) # global average pooling, (N, C, H, W) -> (N, C)

    def forward(self, x):
        x = self.forward_features(x)
        x = self.head(x)
        return x

RepLKNet

https://arxiv.org/abs/2203.06717

受到视觉变换器(ViTs)的最新进展的启发,展示了使用少量大卷积核代替一堆小核可能是一种更强大的范式。提出了五个设计高效大核CNN的指导方针,例如应用重参数化的大深度卷积。

根据这些指导方针,提出了RepLKNet,这是一个纯CNN架构,其核大小达到31×31,与通常使用的3×3形成对比。RepLKNet大大缩小了CNN和ViT之间的性能差距,在ImageNet和一些典型下游任务上取得了与Swin Transformer相当或更优的结果,同时延迟更低。

RepLKNet重参数化大卷积核的类,它允许将大卷积核拆分为多个小卷积核进行训练,之后可以合并为一个大卷积核。

class RepLKBlock(nn.Module):

    def __init__(self, in_channels, dw_channels, block_lk_size, small_kernel, drop_path, small_kernel_merged=False):
        super().__init__()
        self.pw1 = conv_bn_relu(in_channels, dw_channels, 110, groups=1)
        self.pw2 = conv_bn(dw_channels, in_channels, 110, groups=1)
        self.large_kernel = ReparamLargeKernelConv(in_channels=dw_channels, out_channels=dw_channels, kernel_size=block_lk_size,
                                                  stride=1, groups=dw_channels, small_kernel=small_kernel, small_kernel_merged=small_kernel_merged)
        self.lk_nonlinear = nn.ReLU()
        self.prelkb_bn = get_bn(in_channels)
        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
        print('drop path:', self.drop_path)

    def forward(self, x):
        out = self.prelkb_bn(x)
        out = self.pw1(out)
        out = self.large_kernel(out)
        out = self.lk_nonlinear(out)
        out = self.pw2(out)
        return x + self.drop_path(out)

unsetunset模块3:数据增强unsetunset

主要使用torchvision和PIL库,以及timm库中的一些功能,使用这些转换可以提高模型的泛化能力。

这段代码是用于图像处理和增强的Python脚本,主要使用torchvisionPIL库,以及timm库中的一些功能。代码分为几个部分:

JPEGCompression 效果

用于模拟JPEG压缩的效果。它接受一个图像作为输入,以一定的概率(由p参数控制)将其转换为JPEG格式,然后再解码回图像格式。这样可以模拟JPEG压缩带来的图像质量损失。

class JPEGCompression:
    def __init__(self, quality=10, p=0.3):
        self.quality = quality
        self.p = p

    def __call__(self, img):
        if np.random.rand() < self.p:
            img_np = np.array(img)
            _, buffer = cv2.imencode('.jpg', img_np[:, :, ::-1], [int(cv2.IMWRITE_JPEG_QUALITY), self.quality])
            jpeg_img = cv2.imdecode(buffer, 1)
            return Image.fromarray(jpeg_img[:, :, ::-1])
        return img

训练集数据增强

  • img_size参数定义了输出图像的尺寸。
  • scaleratio参数用于RandomResizedCropAndInterpolation,控制随机裁剪的尺度和宽高比。
  • hflipvflip参数控制水平和垂直翻转的概率。
  • auto_augment参数用于指定自动增强策略,如'rand-m9-mstd0.5-inc1'
  • interpolation参数用于指定插值方法。
  • mean参数用于自动增强时的图像均值。
  • jpeg_compression参数用于决定是否应用JPEG压缩。

测试集数据增强

  • create_transforms_inference函数创建一个简单的转换流程,用于推理时的图像处理,包括调整大小和转换为张量。
  • create_transforms_inference1create_transforms_inference5函数分别创建了包含不同旋转和翻转操作的转换流程,用于测试模型的鲁棒性。

unsetunset模块4:模型合并unsetunset

加载训练模型权重,并合并ConvNeXt和RepLKNet模型。

# Trained ConvNeXt and RepLKNet paths (for reference)
convnext_path = './final_model_csv/convnext_end.pth'
replknet_path = './final_model_csv/replk_end.pth'

model = final_model()
model.convnext.load_state_dict(torch.load(convnext_path, map_location='cpu')['state_dict'], strict=True)
model.replknet.load_state_dict(torch.load(replknet_path, map_location='cpu')['state_dict'], strict=True)

if not os.path.exists('./final_model_csv'):
    os.makedirs('./final_model_csv')

torch.save({'state_dict': model.state_dict()}, './final_model_csv/final_model.pth')
快来一起参加Kaggle比赛吧
添加👇微信拉你进群

Coggle数据科学
Coggle全称Communication For Kaggle,专注数据科学领域竞赛相关资讯分享。
 最新文章