当前位置:   article > 正文

【Datawhale AI 夏令营2024--CV】baseline解读

datawhale ai 夏令营

一、赛题背景

        随着人工智能技术的迅猛发展,深度伪造技术(Deepfake)正成为数字世界中的一把双刃剑。这项技术不仅为创意内容的生成提供了新的可能性,同时也对数字安全构成了前所未有的挑战。Deepfake技术可以通过人工智能算法生成高度逼真的图像、视频和音频内容,这些内容看起来与真实的毫无二致。然而,这也意味着虚假信息、欺诈行为和隐私侵害等问题变得更加严重和复杂。

        为了应对这一挑战,我们举办了“外滩大会 - 全球Deepfake攻防挑战赛”。该挑战赛旨在邀请全球的参与者开发、测试和改进更加准确、有效和创新的检测模型,以应对各种类型的Deepfake攻击。这些模型将在真实世界的场景中进行测试,从而推动创新防御策略的发展,提高Deepfake识别的准确性。此次挑战赛不仅是对技术的比拼,更是对全球数字安全的一次重要贡献。我们期待着通过这次比赛,能够激发更多的创新思维和技术突破,共同应对Deepfake带来的安全威胁,保护数字世界的安全与真实性。

二、赛题任务

        在这个赛道中,比赛任务是判断一张人脸图像是否为Deepfake图像,并输出其为Deepfake图像的概率评分。参赛者需要开发和优化检测模型,以应对多样化的Deepfake生成技术和复杂的应用场景,从而提升Deepfake图像检测的准确性和鲁棒性。

三、赛题数据集

1、第一阶段

        在第一阶段,主办方将发布训练集和验证集。参赛者将使用训练集 (train_label.txt) 来训练模型,而验证集 (val_label.txt) 仅用于模型调优。文件的每一行包含两个部分,分别是图片文件名和标签值(label=1 表示Deepfake图像,label=0 表示真实人脸图像)。例如:

train_label.txt

  1. img_name,target
  2. 3381ccbc4df9e7778b720d53a2987014.jpg,1
  3. 63fee8a89581307c0b4fd05a48e0ff79.jpg,0
  4. 7eb4553a58ab5a05ba59b40725c903fd.jpg,0

val_label.txt

  1. img_name,target
  2. cd0e3907b3312f6046b98187fc25f9c7.jpg,1
  3. aa92be19d0adf91a641301cfcce71e8a.jpg,0
  4. 5413a0b706d33ed0208e2e4e2cacaa06.jpg,0

2、第二阶段

        在第一阶段结束后,主办方将发布测试集。在第二阶段,参赛者需要在系统中提交测试集的预测评分文件 (prediction.txt),主办方将在线反馈测试评分结果。文件的每一行包含两个部分,分别是图片文件名和模型预测的Deepfake评分(即样本属于Deepfake图像的概率值)。例如:

prediction.txt

  1. img_name,y_pred
  2. cd0e3907b3312f6046b98187fc25f9c7.jpg,1
  3. aa92be19d0adf91a641301cfcce71e8a.jpg,0.5
  4. 5413a0b706d33ed0208e2e4e2cacaa06.jpg,0.5

3、第三阶段

        在第二阶段结束后,前30名队伍将晋级到第三阶段。在这一阶段,参赛者需要提交代码docker和技术报告。Docker要求包括原始训练代码和测试API(函数输入为图像路径,输出为模型预测的Deepfake评分)。主办方将检查并重新运行算法代码,以重现训练过程和测试结果。

三、评价指标

评估指标

比赛的性能评估主要使用ROC曲线下的AUC(Area under the ROC Curve)作为指标。AUC的取值范围通常在0.5到1之间。若AUC指标不能区分排名,则会使用TPR@FPR=1E-3作为辅助参考。

相关公式:

真阳性率 (TPR):

TPR = TP / (TP + FN)

假阳性率 (FPR):

FPR = FP / (FP + TN)

其中:

  • TP:攻击样本被正确识别为攻击;
  • TN:真实样本被正确识别为真实;
  • FP:真实样本被错误识别为攻击;
  • FN:攻击样本被错误识别为真实。

参考文献:Aghajan, H., Augusto, J. C., & Delgado, R. L. C. (Eds.). (2009). Human-centric interfaces for ambient intelligence. Academic Press.

四、baseline解析

  1. # 统计行数
  2. !wc -l /kaggle/input/deepfake/phase1/trainset_label.txt
  3. !wc -l /kaggle/input/deepfake/phase1/valset_label.txt
  4. # 统计训练集中文件总数
  5. !ls /kaggle/input/deepfake/phase1/trainset/ | wc -l
  6. # 统计验证集中文件总数
  7. !ls /kaggle/input/deepfake/phase1/valset/ | wc -l
  8. !pip install timm
  9. from PIL import Image
  10. Image.open('/kaggle/input/deepfake/phase1/trainset/63fee8a89581307c0b4fd05a48e0ff79.jpg')

  • import torch: 导入PyTorch库。
  • torch.manual_seed(0): 设置PyTorch的随机种子为0,这样可以保证每次运行时生成的随机数是固定的,有助于结果的复现性。
  • torch.backends.cudnn.deterministic = False: 如果使用了CuDNN(CUDA深度神经网络库),此行代码表示不使用确定性算法,可以提高性能。
  • torch.backends.cudnn.benchmark = True: 启用CuDNN的自动寻找最适合当前配置的高效算法,以提升性能。
  1. import torch
  2. torch.manual_seed(0)
  3. torch.backends.cudnn.deterministic = False
  4. torch.backends.cudnn.benchmark = True

导入了一些PyTorch和相关库的模块和函数,用于构建和训练深度学习模型。

  • torchvision.models:包含了常见的预训练模型,如AlexNet、ResNet等。
  • torchvision.transforms:包含常见的图像变换操作,如裁剪、旋转、缩放等。
  • torchvision.datasets:包含常见的数据集,如MNIST、CIFAR-10等。
  • torch.nn:定义了神经网络层的接口和功能。
  • torch.optim:包含了优化器,如SGD、Adam等。
  • torch.autograd.Variable:提供了自动求导机制的变量类型。
  • torch.utils.data.dataset.Dataset:定义了一个抽象的数据集类,用于自定义数据集。
  • timm:一个用于图像模型的库,提供了大量现代化的模型架构。
  • time:Python的时间处理库,通常用于计时或延时操作。
  1. import torchvision.models as models
  2. import torchvision.transforms as transforms
  3. import torchvision.datasets as datasets
  4. import torch.nn as nn
  5. import torch.nn.functional as F
  6. import torch.optim as optim
  7. from torch.autograd import Variable
  8. from torch.utils.data.dataset import Dataset
  9. import timm
  10. import time

导入了一些常见的数据处理和图像处理相关的库。

  • pandas:用于数据操作和分析,例如读取CSV文件。
  • numpy:用于科学计算,支持多维数组和矩阵运算。
  • cv2:OpenCV库,用于图像处理和计算机视觉任务。
  • PIL.Image:Python Imaging Library,用于图像处理,如打开、保存、裁剪等。
  • tqdm_notebook:用于在Jupyter Notebook中显示进度条,便于监控长时间运行的任务进度。
  1. import pandas as pd
  2. import numpy as np
  3. import cv2
  4. from PIL import Image
  5. from tqdm import tqdm_notebook

读取了CSV格式的标签文件,并为每条数据添加了图像路径的列。

  • pd.read_csv():使用pandas库读取CSV文件,将其转换为DataFrame格式。
  • train_label['path']val_label['path']:为每个数据样本添加了图像的完整路径,方便后续读取和处理图像数据。
  1. train_label = pd.read_csv('/kaggle/input/deepfake/phase1/trainset_label.txt')
  2. val_label = pd.read_csv('/kaggle/input/deepfake/phase1/valset_label.txt')
  3. train_label['path'] = '/kaggle/input/deepfake/phase1/trainset/' + train_label['img_name']
  4. val_label['path'] = '/kaggle/input/deepfake/phase1/valset/' + val_label['img_name']
  1. # 统计train_label DataFrame 中 target 列中每个不同取值的频数(即每个类别的样本数量)
  2. train_label['target'].value_counts()
  3. # 对 val_label DataFrame 中 target 列进行统计,获取每个不同类别的样本数量。
  4. val_label['target'].value_counts()
  5. # 一个 pandas DataFrame 的函数调用,用于查看 train_label DataFrame 的前 10 行数据
  6. train_label.head(10)

2、模型训练与验证

  1. class AverageMeter(object):
  2. """Computes and stores the average and current value"""
  3. def __init__(self, name, fmt=':f'):
  4. self.name = name # 存储指标名称
  5. self.fmt = fmt # 格式化字符串,用于打印输出
  6. self.reset() # 调用 reset 方法初始化对象
  7. def reset(self):
  8. self.val = 0 # 当前值初始化为 0
  9. self.avg = 0 # 平均值初始化为 0
  10. self.sum = 0 # 值的总和初始化为 0
  11. self.count = 0 # 更新次数计数初始化为 0
  12. def update(self, val, n=1):
  13. self.val = val # 更新当前值为给定的 val
  14. self.sum += val * n # 将 val * n 累加到总和 sum 中
  15. self.count += n # 更新计数器 count,增加 n
  16. self.avg = self.sum / self.count # 计算新的平均值
  17. def __str__(self):
  18. fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})'
  19. return fmtstr.format(**self.__dict__)
  20. class ProgressMeter(object):
  21. def __init__(self, num_batches, *meters):
  22. self.batch_fmtstr = self._get_batch_fmtstr(num_batches) # 获取批次格式化字符串
  23. self.meters = meters # 存储所有的指标对象
  24. self.prefix = "" # 前缀,用于输出时添加在格式化字符串前
  25. def pr2int(self, batch):
  26. entries = [self.prefix + self.batch_fmtstr.format(batch)] # 添加批次信息
  27. entries += [str(meter) for meter in self.meters] # 添加每个指标的字符串表示
  28. print('\t'.join(entries)) # 打印输出,以制表符分隔每个条目
  29. def _get_batch_fmtstr(self, num_batches):
  30. num_digits = len(str(num_batches // 1)) # 计算批次数的位数
  31. fmt = '{:' + str(num_digits) + 'd}' # 格式化字符串,用于输出批次信息
  32. return '[' + fmt + '/' + fmt.format(num_batches) + ']' # 返回格式化后的批次信息字符串

  1. def validate(val_loader, model, criterion):
  2. # val_loader 是验证数据集的数据加载器,model 是要评估的模型,criterion 是损失函数
  3. batch_time = AverageMeter('Time', ':6.3f')
  4. losses = AverageMeter('Loss', ':.4e')
  5. top1 = AverageMeter('Acc@1', ':6.2f')
  6. progress = ProgressMeter(len(val_loader), batch_time, losses, top1)
  7. # switch to evaluate mode
  8. # 将模型设为评估模式,这会影响一些层(如批归一化层和 dropout),使其在评估时表现正常。
  9. model.eval()
  10. with torch.no_grad():
  11. end = time.time() # 记录每个批次的开始时间
  12. for i, (input, target) in tqdm_notebook(enumerate(val_loader), total=len(val_loader)): # 创建一个进度条来显示验证过程中的迭代进度
  13. # 将输入数据 input 和目标标签 target 移到 GPU 上进行加速计算
  14. input = input.cuda()
  15. target = target.cuda()
  16. # compute output
  17. # 使用模型 model 对输入 input 进行前向传播,得到输出 output
  18. output = model(input)
  19. # 使用损失函数 criterion 计算模型输出 output 和目标标签 target 的损失值 loss
  20. loss = criterion(output, target)
  21. # measure accuracy and record loss
  22. # 计算模型在当前批次上的准确率
  23. acc = (output.argmax(1).view(-1) == target.float().view(-1)).float().mean() * 100
  24. # 使用 AverageMeter 更新损失和准确率的统计信息
  25. losses.update(loss.item(), input.size(0))
  26. top1.update(acc, input.size(0))
  27. # measure elapsed time
  28. # 使用 AverageMeter 更新每个批次的运行时间
  29. batch_time.update(time.time() - end)
  30. end = time.time()
  31. # TODO: this should also be done with the ProgressMeter
  32. # 打印当前验证集的准确率(平均值)
  33. print(' * Acc@1 {top1.avg:.3f}'
  34. .format(top1=top1))
  35. return top1
  1. # 模型推理预测
  2. def predict(test_loader, model, tta=10):
  3. # switch to evaluate mode
  4. model.eval() # 将模型切换到评估模式
  5. test_pred_tta = None # 初始化,用来存储测试时间增强后的预测结果
  6. for _ in range(tta): # 开始进行测试时间增强(TTA)的循环
  7. test_pred = [] # 用来存储单次测试时间增强的每个样本的预测结果
  8. # 上下文管理器,确保在进行推理时不计算梯度,以节省内存和提高速度
  9. with torch.no_grad():
  10. end = time.time()
  11. for i, (input, target) in tqdm_notebook(enumerate(test_loader), total=len(test_loader)): # 枚举测试数据加载器中的每个批次数据
  12. input = input.cuda()
  13. target = target.cuda()
  14. # compute output
  15. output = model(input) # 使用深度学习模型 model 进行输入数据 input 的预测,得到模型的输出
  16. output = F.softmax(output, dim=1) # 对模型的输出进行 softmax 操作,将其转换为概率分布。dim=1 表示在第一维(通常是类别维度)上进行 softmax 操作
  17. output = output.data.cpu().numpy() # 将输出 output 转换为 NumPy 数组,并将其从 GPU 上移动到 CPU 上
  18. test_pred.append(output) # 将当前批次的预测结果 output 添加到 test_pred 列表中
  19. test_pred = np.vstack(test_pred) # 将 test_pred 列表中的每个数组(每个批次的预测结果)垂直堆叠起来,形成一个大的二维数组,行数等于所有预测结果的总数
  20. if test_pred_tta is None:
  21. test_pred_tta = test_pred
  22. else:
  23. test_pred_tta += test_pred # 累积所有测试时间增强的预测结果
  24. return test_pred_tta
  1. def train(train_loader, model, criterion, optimizer, epoch):
  2. # 创建用于测量训练过程中时间、损失和准确率的平均值计量器
  3. batch_time = AverageMeter('Time', ':6.3f')
  4. losses = AverageMeter('Loss', ':.4e')
  5. top1 = AverageMeter('Acc@1', ':6.2f')
  6. # 创建进度条显示训练进度
  7. progress = ProgressMeter(len(train_loader), batch_time, losses, top1)
  8. # 将模型设置为训练模式
  9. model.train()
  10. # 初始化计时器
  11. end = time.time()
  12. # 遍历训练数据集
  13. for i, (input, target) in enumerate(train_loader):
  14. # 将输入数据和目标标签移动到GPU上(如果可用),并设置为非阻塞操作
  15. input = input.cuda(non_blocking=True)
  16. target = target.cuda(non_blocking=True)
  17. # 计算模型的输出
  18. output = model(input)
  19. # 计算损失值
  20. loss = criterion(output, target)
  21. # 记录损失值
  22. losses.update(loss.item(), input.size(0))
  23. # 计算准确率并记录
  24. acc = (output.argmax(1).view(-1) == target.float().view(-1)).float().mean() * 100
  25. top1.update(acc, input.size(0))
  26. # 梯度清零,进行反向传播和优化
  27. optimizer.zero_grad()
  28. loss.backward()
  29. optimizer.step()
  30. # 测量经过的时间
  31. batch_time.update(time.time() - end)
  32. end = time.time()
  33. # 每隔100个batch打印一次训练进度
  34. if i % 100 == 0:
  35. progress.pr2int(i)
  1. class FFDIDataset(Dataset):
  2. def __init__(self, img_path, img_label, transform=None):
  3. self.img_path = img_path
  4. self.img_label = img_label
  5. # 设置数据集的变换操作 transform
  6. if transform is not None:
  7. self.transform = transform
  8. else:
  9. self.transform = None
  10. def __getitem__(self, index):
  11. # 将其转换为 RGB 模式的 PIL.Image 对象
  12. img = Image.open(self.img_path[index]).convert('RGB')
  13. if self.transform is not None:
  14. img = self.transform(img)
  15. # 标签数据首先通过 np.array 转换为 NumPy 数组,然后再通过 torch.from_numpy 转换为 PyTorch 的 Tensor 类型
  16. return img, torch.from_numpy(np.array(self.img_label[index]))
  17. # 返回数据集的长度,即数据集中图像的数量
  18. def __len__(self):
  19. return len(self.img_path)

3、加载模型

  1. import timm
  2. model = timm.create_model('resnet18', pretrained=True, num_classes=2)
  3. model = model.cuda()
  1. train_loader = torch.utils.data.DataLoader(
  2. FFDIDataset(train_label['path'].head(1000), train_label['target'].head(1000),
  3. transforms.Compose([
  4. transforms.Resize((256, 256)), # 将图像大小调整为256x256像素
  5. transforms.RandomHorizontalFlip(), # 随机水平翻转图像
  6. transforms.RandomVerticalFlip(), # 随机垂直翻转图像
  7. transforms.ToTensor(), # 将图像转换为Tensor格式
  8. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  9. ])
  10. ), batch_size=40, shuffle=True, num_workers=4, pin_memory=True
  11. # batch_size=40:每个批次的样本数量为40。
  12. # shuffle=True:每个 epoch 开始时打乱数据,有助于模型学习。
  13. # num_workers=4:使用多进程加载数据,加快数据读取速度。
  14. # pin_memory=True:将数据加载到 CUDA 固定内存中,加快 GPU 加速
  15. )
  1. val_loader = torch.utils.data.DataLoader(
  2. FFDIDataset(val_label['path'].head(1000), val_label['target'].head(1000),
  3. transforms.Compose([
  4. transforms.Resize((256, 256)), # 将图像大小调整为256x256像素
  5. transforms.ToTensor(), # 将图像转换为Tensor格式
  6. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  7. ])
  8. ), batch_size=40, shuffle=False, num_workers=4, pin_memory=True
  9. )
  1. # 定义了损失函数,这里使用交叉熵损失函数 CrossEntropyLoss
  2. criterion = nn.CrossEntropyLoss().cuda()
  3. # 使用 Adam 优化器来优化模型参数,学习率为 0.005
  4. optimizer = torch.optim.Adam(model.parameters(), 0.005)
  5. # 设置了学习率调度器,每 4 个 epoch 学习率乘以 0.85
  6. scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.85)
  7. best_acc = 0.0
  8. for epoch in range(2):
  9. scheduler.step() # 调用学习率调度器的 step 方法,更新学习率
  10. print('Epoch: ', epoch)
  11. train(train_loader, model, criterion, optimizer, epoch)
  12. val_acc = validate(val_loader, model, criterion)
  13. # 如果当前验证集准确率高于历史最佳准确率,则更新最佳准确率并保存模型参数
  14. if val_acc.avg.item() > best_acc:
  15. best_acc = round(val_acc.avg.item(), 2)
  16. torch.save(model.state_dict(), f'./model_{best_acc}.pt')
  1. # 创建了一个名为 test_loader 的数据加载器对象,用于在训练或评估期间批量加载数据
  2. test_loader = torch.utils.data.DataLoader(
  3. # 将多个图像变换组合在一起的类,这里的变换按顺序作用于每个图像
  4. FFDIDataset(val_label['path'], val_label['target'],
  5. transforms.Compose([
  6. transforms.Resize((256, 256)), # 将输入图像大小调整为 (256, 256) 像素
  7. transforms.ToTensor(), # 将图像数据转换为 PyTorch 张量
  8. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  9. ]) # 对图像进行标准化,使用给定的均值和标准差
  10. ), batch_size=40, shuffle=False, num_workers=4, pin_memory=True
  11. # batch_size=40:每个批次包含的图像数量为 40。
  12. # shuffle=False:不打乱数据顺序,用于验证数据集通常不需要打乱。
  13. # num_workers=4:用于数据加载的线程数目,这可以加速数据加载过程。
  14. # pin_memory=True:如果设为 True,数据将会被加载到 CUDA 的固定内存区域,这可以提升 GPU 数据加载的速度。
  15. )
  16. # 使用 predict 函数对验证数据集进行预测,并将预测结果存储在 val_label 数据帧的 'y_pred' 列中
  17. # 选择所有行的第二列(索引为 1)作为预测概率
  18. val_label['y_pred'] = predict(test_loader, model, 1)[:, 1]
  19. # 将'img_name' 和 'y_pred' 两列保存到submit.csv文件中,不包括行索引
  20. val_label[['img_name', 'y_pred']].to_csv('submit.csv', index=None)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/978125
推荐阅读
相关标签
  

闽ICP备14008679号