赞
踩
随着人工智能技术的迅猛发展,深度伪造技术(Deepfake)正成为数字世界中的一把双刃剑。这项技术不仅为创意内容的生成提供了新的可能性,同时也对数字安全构成了前所未有的挑战。Deepfake技术可以通过人工智能算法生成高度逼真的图像、视频和音频内容,这些内容看起来与真实的毫无二致。然而,这也意味着虚假信息、欺诈行为和隐私侵害等问题变得更加严重和复杂。
为了应对这一挑战,我们举办了“外滩大会 - 全球Deepfake攻防挑战赛”。该挑战赛旨在邀请全球的参与者开发、测试和改进更加准确、有效和创新的检测模型,以应对各种类型的Deepfake攻击。这些模型将在真实世界的场景中进行测试,从而推动创新防御策略的发展,提高Deepfake识别的准确性。此次挑战赛不仅是对技术的比拼,更是对全球数字安全的一次重要贡献。我们期待着通过这次比赛,能够激发更多的创新思维和技术突破,共同应对Deepfake带来的安全威胁,保护数字世界的安全与真实性。
在这个赛道中,比赛任务是判断一张人脸图像是否为Deepfake图像,并输出其为Deepfake图像的概率评分。参赛者需要开发和优化检测模型,以应对多样化的Deepfake生成技术和复杂的应用场景,从而提升Deepfake图像检测的准确性和鲁棒性。
在第一阶段,主办方将发布训练集和验证集。参赛者将使用训练集 (train_label.txt) 来训练模型,而验证集 (val_label.txt) 仅用于模型调优。文件的每一行包含两个部分,分别是图片文件名和标签值(label=1 表示Deepfake图像,label=0 表示真实人脸图像)。例如:
train_label.txt
- img_name,target
- 3381ccbc4df9e7778b720d53a2987014.jpg,1
- 63fee8a89581307c0b4fd05a48e0ff79.jpg,0
- 7eb4553a58ab5a05ba59b40725c903fd.jpg,0
- …
val_label.txt
- img_name,target
- cd0e3907b3312f6046b98187fc25f9c7.jpg,1
- aa92be19d0adf91a641301cfcce71e8a.jpg,0
- 5413a0b706d33ed0208e2e4e2cacaa06.jpg,0
- …
在第一阶段结束后,主办方将发布测试集。在第二阶段,参赛者需要在系统中提交测试集的预测评分文件 (prediction.txt),主办方将在线反馈测试评分结果。文件的每一行包含两个部分,分别是图片文件名和模型预测的Deepfake评分(即样本属于Deepfake图像的概率值)。例如:
prediction.txt
- img_name,y_pred
- cd0e3907b3312f6046b98187fc25f9c7.jpg,1
- aa92be19d0adf91a641301cfcce71e8a.jpg,0.5
- 5413a0b706d33ed0208e2e4e2cacaa06.jpg,0.5
- …
在第二阶段结束后,前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:攻击样本被错误识别为真实。
- # 统计行数
- !wc -l /kaggle/input/deepfake/phase1/trainset_label.txt
- !wc -l /kaggle/input/deepfake/phase1/valset_label.txt
-
- # 统计训练集中文件总数
- !ls /kaggle/input/deepfake/phase1/trainset/ | wc -l
- # 统计验证集中文件总数
- !ls /kaggle/input/deepfake/phase1/valset/ | wc -l
-
- !pip install timm
-
- from PIL import Image
- 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的自动寻找最适合当前配置的高效算法,以提升性能。- import torch
- torch.manual_seed(0)
- torch.backends.cudnn.deterministic = False
- torch.backends.cudnn.benchmark = True
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的时间处理库,通常用于计时或延时操作。- import torchvision.models as models
- import torchvision.transforms as transforms
- import torchvision.datasets as datasets
- import torch.nn as nn
- import torch.nn.functional as F
- import torch.optim as optim
- from torch.autograd import Variable
- from torch.utils.data.dataset import Dataset
- import timm
- import time
pandas
:用于数据操作和分析,例如读取CSV文件。numpy
:用于科学计算,支持多维数组和矩阵运算。cv2
:OpenCV库,用于图像处理和计算机视觉任务。PIL.Image
:Python Imaging Library,用于图像处理,如打开、保存、裁剪等。tqdm_notebook
:用于在Jupyter Notebook中显示进度条,便于监控长时间运行的任务进度。- import pandas as pd
- import numpy as np
- import cv2
- from PIL import Image
- from tqdm import tqdm_notebook
pd.read_csv()
:使用pandas库读取CSV文件,将其转换为DataFrame格式。train_label['path']
和val_label['path']
:为每个数据样本添加了图像的完整路径,方便后续读取和处理图像数据。- train_label = pd.read_csv('/kaggle/input/deepfake/phase1/trainset_label.txt')
- val_label = pd.read_csv('/kaggle/input/deepfake/phase1/valset_label.txt')
-
- train_label['path'] = '/kaggle/input/deepfake/phase1/trainset/' + train_label['img_name']
- val_label['path'] = '/kaggle/input/deepfake/phase1/valset/' + val_label['img_name']
- # 统计train_label DataFrame 中 target 列中每个不同取值的频数(即每个类别的样本数量)
- train_label['target'].value_counts()
-
- # 对 val_label DataFrame 中 target 列进行统计,获取每个不同类别的样本数量。
- val_label['target'].value_counts()
-
- # 一个 pandas DataFrame 的函数调用,用于查看 train_label DataFrame 的前 10 行数据
- train_label.head(10)
- class AverageMeter(object):
- """Computes and stores the average and current value"""
- def __init__(self, name, fmt=':f'):
- self.name = name # 存储指标名称
- self.fmt = fmt # 格式化字符串,用于打印输出
- self.reset() # 调用 reset 方法初始化对象
-
- def reset(self):
- self.val = 0 # 当前值初始化为 0
- self.avg = 0 # 平均值初始化为 0
- self.sum = 0 # 值的总和初始化为 0
- self.count = 0 # 更新次数计数初始化为 0
-
- def update(self, val, n=1):
- self.val = val # 更新当前值为给定的 val
- self.sum += val * n # 将 val * n 累加到总和 sum 中
- self.count += n # 更新计数器 count,增加 n
- self.avg = self.sum / self.count # 计算新的平均值
-
- def __str__(self):
- fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})'
- return fmtstr.format(**self.__dict__)
-
- class ProgressMeter(object):
- def __init__(self, num_batches, *meters):
- self.batch_fmtstr = self._get_batch_fmtstr(num_batches) # 获取批次格式化字符串
- self.meters = meters # 存储所有的指标对象
- self.prefix = "" # 前缀,用于输出时添加在格式化字符串前
-
-
- def pr2int(self, batch):
- entries = [self.prefix + self.batch_fmtstr.format(batch)] # 添加批次信息
- entries += [str(meter) for meter in self.meters] # 添加每个指标的字符串表示
- print('\t'.join(entries)) # 打印输出,以制表符分隔每个条目
-
- def _get_batch_fmtstr(self, num_batches):
- num_digits = len(str(num_batches // 1)) # 计算批次数的位数
- fmt = '{:' + str(num_digits) + 'd}' # 格式化字符串,用于输出批次信息
- return '[' + fmt + '/' + fmt.format(num_batches) + ']' # 返回格式化后的批次信息字符串
- def validate(val_loader, model, criterion):
- # val_loader 是验证数据集的数据加载器,model 是要评估的模型,criterion 是损失函数
- batch_time = AverageMeter('Time', ':6.3f')
- losses = AverageMeter('Loss', ':.4e')
- top1 = AverageMeter('Acc@1', ':6.2f')
- progress = ProgressMeter(len(val_loader), batch_time, losses, top1)
-
- # switch to evaluate mode
- # 将模型设为评估模式,这会影响一些层(如批归一化层和 dropout),使其在评估时表现正常。
- model.eval()
-
- with torch.no_grad():
- end = time.time() # 记录每个批次的开始时间
- for i, (input, target) in tqdm_notebook(enumerate(val_loader), total=len(val_loader)): # 创建一个进度条来显示验证过程中的迭代进度
- # 将输入数据 input 和目标标签 target 移到 GPU 上进行加速计算
- input = input.cuda()
- target = target.cuda()
-
- # compute output
- # 使用模型 model 对输入 input 进行前向传播,得到输出 output
- output = model(input)
- # 使用损失函数 criterion 计算模型输出 output 和目标标签 target 的损失值 loss
- loss = criterion(output, target)
-
- # measure accuracy and record loss
- # 计算模型在当前批次上的准确率
- acc = (output.argmax(1).view(-1) == target.float().view(-1)).float().mean() * 100
-
- # 使用 AverageMeter 更新损失和准确率的统计信息
- losses.update(loss.item(), input.size(0))
- top1.update(acc, input.size(0))
-
- # measure elapsed time
- # 使用 AverageMeter 更新每个批次的运行时间
- batch_time.update(time.time() - end)
- end = time.time()
-
- # TODO: this should also be done with the ProgressMeter
- # 打印当前验证集的准确率(平均值)
- print(' * Acc@1 {top1.avg:.3f}'
- .format(top1=top1))
- return top1
- # 模型推理预测
- def predict(test_loader, model, tta=10):
- # switch to evaluate mode
- model.eval() # 将模型切换到评估模式
-
- test_pred_tta = None # 初始化,用来存储测试时间增强后的预测结果
- for _ in range(tta): # 开始进行测试时间增强(TTA)的循环
- test_pred = [] # 用来存储单次测试时间增强的每个样本的预测结果
-
- # 上下文管理器,确保在进行推理时不计算梯度,以节省内存和提高速度
- with torch.no_grad():
- end = time.time()
- for i, (input, target) in tqdm_notebook(enumerate(test_loader), total=len(test_loader)): # 枚举测试数据加载器中的每个批次数据
- input = input.cuda()
- target = target.cuda()
-
- # compute output
- output = model(input) # 使用深度学习模型 model 进行输入数据 input 的预测,得到模型的输出
- output = F.softmax(output, dim=1) # 对模型的输出进行 softmax 操作,将其转换为概率分布。dim=1 表示在第一维(通常是类别维度)上进行 softmax 操作
- output = output.data.cpu().numpy() # 将输出 output 转换为 NumPy 数组,并将其从 GPU 上移动到 CPU 上
-
- test_pred.append(output) # 将当前批次的预测结果 output 添加到 test_pred 列表中
- test_pred = np.vstack(test_pred) # 将 test_pred 列表中的每个数组(每个批次的预测结果)垂直堆叠起来,形成一个大的二维数组,行数等于所有预测结果的总数
-
- if test_pred_tta is None:
- test_pred_tta = test_pred
- else:
- test_pred_tta += test_pred # 累积所有测试时间增强的预测结果
-
- return test_pred_tta
- def train(train_loader, model, criterion, optimizer, epoch):
- # 创建用于测量训练过程中时间、损失和准确率的平均值计量器
- batch_time = AverageMeter('Time', ':6.3f')
- losses = AverageMeter('Loss', ':.4e')
- top1 = AverageMeter('Acc@1', ':6.2f')
-
- # 创建进度条显示训练进度
- progress = ProgressMeter(len(train_loader), batch_time, losses, top1)
-
- # 将模型设置为训练模式
- model.train()
-
- # 初始化计时器
- end = time.time()
-
- # 遍历训练数据集
- for i, (input, target) in enumerate(train_loader):
-
- # 将输入数据和目标标签移动到GPU上(如果可用),并设置为非阻塞操作
- input = input.cuda(non_blocking=True)
- target = target.cuda(non_blocking=True)
-
- # 计算模型的输出
- output = model(input)
-
- # 计算损失值
- loss = criterion(output, target)
-
- # 记录损失值
- losses.update(loss.item(), input.size(0))
-
- # 计算准确率并记录
- acc = (output.argmax(1).view(-1) == target.float().view(-1)).float().mean() * 100
- top1.update(acc, input.size(0))
-
- # 梯度清零,进行反向传播和优化
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
-
- # 测量经过的时间
- batch_time.update(time.time() - end)
- end = time.time()
-
- # 每隔100个batch打印一次训练进度
- if i % 100 == 0:
- progress.pr2int(i)
- class FFDIDataset(Dataset):
- def __init__(self, img_path, img_label, transform=None):
- self.img_path = img_path
- self.img_label = img_label
-
- # 设置数据集的变换操作 transform
- if transform is not None:
- self.transform = transform
- else:
- self.transform = None
-
- def __getitem__(self, index):
- # 将其转换为 RGB 模式的 PIL.Image 对象
- img = Image.open(self.img_path[index]).convert('RGB')
-
- if self.transform is not None:
- img = self.transform(img)
-
- # 标签数据首先通过 np.array 转换为 NumPy 数组,然后再通过 torch.from_numpy 转换为 PyTorch 的 Tensor 类型
- return img, torch.from_numpy(np.array(self.img_label[index]))
-
- # 返回数据集的长度,即数据集中图像的数量
- def __len__(self):
- return len(self.img_path)
- import timm
- model = timm.create_model('resnet18', pretrained=True, num_classes=2)
- model = model.cuda()
- train_loader = torch.utils.data.DataLoader(
- FFDIDataset(train_label['path'].head(1000), train_label['target'].head(1000),
- transforms.Compose([
- transforms.Resize((256, 256)), # 将图像大小调整为256x256像素
- transforms.RandomHorizontalFlip(), # 随机水平翻转图像
- transforms.RandomVerticalFlip(), # 随机垂直翻转图像
- transforms.ToTensor(), # 将图像转换为Tensor格式
- transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
- ])
- ), batch_size=40, shuffle=True, num_workers=4, pin_memory=True
- # batch_size=40:每个批次的样本数量为40。
- # shuffle=True:每个 epoch 开始时打乱数据,有助于模型学习。
- # num_workers=4:使用多进程加载数据,加快数据读取速度。
- # pin_memory=True:将数据加载到 CUDA 固定内存中,加快 GPU 加速
- )
- val_loader = torch.utils.data.DataLoader(
- FFDIDataset(val_label['path'].head(1000), val_label['target'].head(1000),
- transforms.Compose([
- transforms.Resize((256, 256)), # 将图像大小调整为256x256像素
- transforms.ToTensor(), # 将图像转换为Tensor格式
- transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
- ])
- ), batch_size=40, shuffle=False, num_workers=4, pin_memory=True
- )
- # 定义了损失函数,这里使用交叉熵损失函数 CrossEntropyLoss
- criterion = nn.CrossEntropyLoss().cuda()
-
- # 使用 Adam 优化器来优化模型参数,学习率为 0.005
- optimizer = torch.optim.Adam(model.parameters(), 0.005)
-
- # 设置了学习率调度器,每 4 个 epoch 学习率乘以 0.85
- scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.85)
-
- best_acc = 0.0
- for epoch in range(2):
- scheduler.step() # 调用学习率调度器的 step 方法,更新学习率
- print('Epoch: ', epoch)
-
- train(train_loader, model, criterion, optimizer, epoch)
- val_acc = validate(val_loader, model, criterion)
-
- # 如果当前验证集准确率高于历史最佳准确率,则更新最佳准确率并保存模型参数
- if val_acc.avg.item() > best_acc:
- best_acc = round(val_acc.avg.item(), 2)
- torch.save(model.state_dict(), f'./model_{best_acc}.pt')
- # 创建了一个名为 test_loader 的数据加载器对象,用于在训练或评估期间批量加载数据
- test_loader = torch.utils.data.DataLoader(
-
- # 将多个图像变换组合在一起的类,这里的变换按顺序作用于每个图像
- FFDIDataset(val_label['path'], val_label['target'],
-
- transforms.Compose([
- transforms.Resize((256, 256)), # 将输入图像大小调整为 (256, 256) 像素
- transforms.ToTensor(), # 将图像数据转换为 PyTorch 张量
- transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
- ]) # 对图像进行标准化,使用给定的均值和标准差
-
- ), batch_size=40, shuffle=False, num_workers=4, pin_memory=True
- # batch_size=40:每个批次包含的图像数量为 40。
- # shuffle=False:不打乱数据顺序,用于验证数据集通常不需要打乱。
- # num_workers=4:用于数据加载的线程数目,这可以加速数据加载过程。
- # pin_memory=True:如果设为 True,数据将会被加载到 CUDA 的固定内存区域,这可以提升 GPU 数据加载的速度。
- )
-
- # 使用 predict 函数对验证数据集进行预测,并将预测结果存储在 val_label 数据帧的 'y_pred' 列中
- # 选择所有行的第二列(索引为 1)作为预测概率
- val_label['y_pred'] = predict(test_loader, model, 1)[:, 1]
-
- # 将'img_name' 和 'y_pred' 两列保存到submit.csv文件中,不包括行索引
- val_label[['img_name', 'y_pred']].to_csv('submit.csv', index=None)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。