本次比赛为“外滩大会 - 全球Deepfake攻防挑战赛”的比赛旨在应对深度伪造技术(Deepfake)在数字安全中带来的挑战。Deepfake技术利用人工智能生成逼真的图像、视频和音频内容,极大增加了虚假信息、欺诈行为和隐私侵害的风险。比赛邀请全球参与者开发、测试和改进检测模型,以准确判断人脸图像是否为Deepfake,并输出其为Deepfake的概率评分。参赛者需要应对多样化的Deepfake生成技术和复杂的应用场景,推动创新防御策略的发展,提高Deepfake图像检测的准确性和鲁棒性。通过比赛,旨在激发创新思维和技术突破,共同保护数字世界的安全与真实性。
比赛连接:Deepfake-FFDV-音视频赛题 baseline (kaggle.com)
1. 数据准备
在深度学习中,数据准备是非常关键的一步。值得注意的是这一步需要过滤掉不存在的视频路径。其他代码(函数)的解释:
这段代码定义了两个辅助类:AverageMeter用于计算和存储平均值及当前值,而ProgressMeter则用于展示训练过程中的进度和指标。这些类通常在机器学习模型的训练循环中使用,帮助实时跟踪和记录训练过程中的关键指标,如损失和准确率,同时提供直观的进度显示。
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()
def reset(self):
self.val = 0
self.avg = 0
self.sum = 0
self.count = 0
def update(self, val, n=1):
self.val = val
self.sum += val * n
self.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) + ']'
下面这段代码定义了用于深度学习模型训练、验证和预测的关键函数。`validate`函数用于评估模型在验证集上的性能,`predict`函数用于生成测试集的预测结果,而`train`函数则负责模型的训练过程。这些函数利用`AverageMeter`来实时计算和记录时间、损失和准确率,同时通过`ProgressMeter`类来展示训练进度和指标,以便有效地监控和调整模型训练过程。
def validate(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
model.eval()
with torch.no_grad():
end = time.time()
for i, (input, target) in enumerate(val_loader):
input = input.cuda()
target = target.cuda()
# compute output
output = model(input)
loss = criterion(output, target)
# measure accuracy and record loss
acc = (output.argmax(1).view(-1) == target.float().view(-1)).float().mean() * 100
losses.update(loss.item(), input.size(0))
top1.update(acc, input.size(0))
# measure elapsed time
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):
test_pred = []
with torch.no_grad():
end = time.time()
for i, (input, target) in enumerate(test_loader):
input = input.cuda()
target = target.cuda()
# compute output
output = model(input)
output = F.softmax(output, dim=1)
output = output.data.cpu().numpy()
test_pred.append(output)
test_pred = np.vstack(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)
# switch to train mode
model.train()
end = time.time()
for i, (input, target) in enumerate(train_loader):
input = input.cuda(non_blocking=True)
target = target.cuda(non_blocking=True)
# compute output
print(input.shape)
output = model(input)
loss = criterion(output, target)
# measure accuracy and record loss
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))
# compute gradient and do SGD step
optimizer.zero_grad()
loss.backward()
optimizer.step()
# measure elapsed time
batch_time.update(time.time() - end)
end = time.time()
if i % 100 == 0:
progress.pr2int(i)
2. 定义生成MEL频谱图的函数
它这里主要是处理的是视频中音频,而非原视频,使用的是一种伪二维图片的形式来进行检测,当然你也可以使用语音原来的一维数据音频数据。但对于深度学习模型而言,直接使用波形数据可能不够高效和有效。生成MEL频谱图是将音频数据转换为图像数据的常见方法之一。具体的是通过使用Librosa库提取音频特征并生成MEL频谱图,为模型提供了更丰富的输入信息,从而提升了模型在音频任务中的表现。
def generate_mel_spectrogram(video_path, n_mels=128, fmax=8000, target_size=(256, 256)):
# 提取音频
audio_path = 'extracted_audio.wav'
video = mp.VideoFileClip(video_path)
video.audio.write_audiofile(audio_path, verbose=False, logger=None)
# 加载音频文件
y, sr = librosa.load(audio_path)
# 生成MEL频谱图
S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=n_mels)
# 将频谱图转换为dB单位
S_dB = librosa.power_to_db(S, ref=np.max)
# 归一化到0-255之间
S_dB_normalized = cv2.normalize(S_dB, None, 0, 255, cv2.NORM_MINMAX)
# 将浮点数转换为无符号8位整型
S_dB_normalized = S_dB_normalized.astype(np.uint8)
# 缩放到目标大小
img_resized = cv2.resize(S_dB_normalized, target_size, interpolation=cv2.INTER_LINEAR)
return img_resized
3. 模型初始化和训练
下面段代码实现了一个基于ResNet-18模型的深度学习训练流程,用于二分类任务。它首先定义了数据加载器,包括训练集和验证集的数据预处理和加载。然后选择了ResNet-18作为基础模型,并配置了损失函数为交叉熵,优化器为Adam,并使用学习率调度器进行学习率管理。每个epoch中,通过调用训练函数进行模型训练,然后通过验证函数评估模型性能,并保存在验证集上表现最好的模型权重。
当然你也可以试试其他模型,比如vgg系列,resnet系列。尝试调一下超参数之类的。
train_loader = torch.utils.data.DataLoader(
FFDIDataset(train_label['path'].values, train_label['target'].values,
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])
])
), batch_size=40, shuffle=True, num_workers=12, pin_memory=True
)
val_loader = torch.utils.data.DataLoader(
FFDIDataset(val_label['path'].values, val_label['target'].values,
transforms.Compose([
transforms.Resize((256, 256)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
), batch_size=40, shuffle=False, num_workers=10, pin_memory=True
)
model = timm.create_model('resnet18', pretrained=True, num_classes=2)
model = model.cuda()
criterion = nn.CrossEntropyLoss().cuda()
optimizer = torch.optim.Adam(model.parameters(), 0.003)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.85)
best_acc = 0.0
for epoch in range(60):
scheduler.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')
4. 生成预测结果
最后,生成预测结果并将其保存为提交文件是项目完成的最后一步。
val_pred = predict(val_loader, model, 1)[:, 1]
val_label["y_pred"] = val_pred
submit = pd.read_csv("/kaggle/input/multi-ffdv/prediction.txt.csv")
merged_df = submit.merge(val_label[['video_name', 'y_pred']], on='video_name', suffixes=('', '_df2'), how='left', )
merged_df['y_pred'] = merged_df['y_pred_df2'].combine_first(merged_df['y_pred'])
merged_df[['video_name', 'y_pred']].to_csv('submit.csv', index=None)
最终你可以在kaggle上提交你的结果,看看排名!!!!
5、思考
官方提供的代码是从音频的角度出发的,可以尝试换各种模型(要规范),调参以及可以从数据上入手,怎么能够很好的提出音频的特征。当然你也可以从视频的角度出发,直接处理视频,现在也有甚多方法,c3d/i3d,还有一些比较新的方法。也可以将两种模态结合起来。