Task1:入门Deepfake & 初步熟悉Baseline#AI夏令营 #Datawhale #夏令营

本次比赛为“外滩大会 - 全球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,还有一些比较新的方法。也可以将两种模态结合起来。

  • 16
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值