Compartilhamento de tecnologia

[Prática de aprendizado de máquina] Datawhale Summer Camp 2: ataque e defesa de áudio e vídeo (deepfake) Explicação da frase de base

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

#Datawhale #AIsummercamp#campo de verão

1. Breve introdução às questões do concurso

A tarefa da competição é determinar se uma imagem facial é uma imagem Deepfake e gerar uma pontuação de probabilidade de que seja uma imagem Deepfake. Os participantes precisam desenvolver e otimizar modelos de detecção para lidar com diversas tecnologias de geração de deepfake e cenários de aplicação complexos, melhorando assim a precisão e a robustez da detecção de imagens deepfake.

2. Conjunto de dados de questões de concorrência

O arquivo de rótulo train_label.txt do conjunto de treinamento é usado para treinar o modelo, enquanto o arquivo de rótulo val_label.txt do conjunto de validação é usado apenas para ajuste do modelo. Por exemplo, em train_label.txt ou val_label.txt, cada linha contém duas partes, separadas por vírgulas. A primeira parte é o nome do arquivo (sufixo .mp4), a segunda parte é o valor real.
Um valor alvo de 1 indica áudio e vídeo profundamente falsos, e um valor alvo de 0 indica áudio e vídeo reais.

Abaixo estão exemplos de train_label.txt e val_label.txt:

train_label.txt

video_name,target
96b04c80704f02cb426076b3f624b69e.mp4,0
16fe4cf5ae8b3928c968a5d11e870360.mp4,1
  • 1
  • 2
  • 3
  • 4
  • 5

val_label.txt

video_name,target
f859cb3510c69513d5c57c6934bc9968.mp4,0
50ae26b3f3ea85babb2f9dde840830e2.mp4,1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Cada linha do arquivo contém duas partes, separadas por vírgulas. A primeira parte é o nome do arquivo de vídeo e a segunda parte é a pontuação do deepfake correspondente à previsão do modelo (ou seja, o valor de probabilidade da amostra pertencente ao vídeo deepfake). Consulte o modelo de envio abaixo:

prediction.csv

video_name,score
658042526e6d0c199adc7bfeb1f7c888.mp4,0.123456
a20cf2d7dea580d0affc4d85c9932479.mp4,0.123456
  • 1
  • 2
  • 3
  • 4
  • 5

A segunda fase segue a primeira fase, na qual o conjunto de testes públicos é lançado. Os participantes precisam enviar o arquivo de pontuação de previsão Prediction_test.csv do conjunto de testes para o sistema e comentar os resultados da pontuação do teste on-line em tempo real.

Após a segunda fase, as 30 melhores equipes avançam para a terceira fase. Nesta fase, os competidores precisam enviar o código docker e relatórios técnicos. Os requisitos do Docker incluem o código de treinamento original e a API de teste (a entrada da função é o caminho da imagem e a saída é a pontuação deepfake prevista pelo modelo). O patrocinador verificará e executará novamente o código do algoritmo para reproduzir o processo de treinamento e os resultados do teste.

Apenas um único modelo pode ser enviado e os parâmetros de rede válidos não devem exceder 200M (usetopoParâmetros do modelo de estatísticas da ferramenta).

Somente o treinamento pré-modelo com ImageNet1K é permitido. Amostras estendidas geradas com base no conjunto de treinamento publicado (por meio de ferramentas de aumento de dados/deepfake) podem ser usadas para treinamento, mas essas ferramentas precisam ser enviadas para reprodução no terceiro estágio.

3. Indicadores de avaliação

O índice de avaliação utiliza principalmente a AUC sob a curva ROC como índice. O intervalo de valores da AUC está geralmente dentro.0.5-1entre, caso contrário pensamosEste não é um bom modelo de aprendizado de máquina . Quanto mais próximo o AUC estiver de 1, melhor será o modelo. Se a AUC apresentar resultados de classificação ambíguos, usamos **TPR (taxa de verdadeiro positivo)** como referência auxiliar. Claro, o método correspondente é o FPR.

Pontuação F1É também um indicador ao qual podemos nos referir: é a taxa de precisão e a taxa de recallmédia harmônica

F 1 _ S núcleo = 2 ∗ ( TP ) / ( 2 TP + FN + FP ) F1_Pontuação = 2*(TP)/(2TP+FN+FP)F1_Sessencial=2(TP)/(2TP+FN+FP)

Antes de aprender o aprendizado de máquina, devemos revisar dois conceitos importantes:PrecisãoeLembrar

Precisão P recisão = TPTP + FP Precisão = frac{TP}{TP+FP}Pgravandoeueeuonãoão=TP+FPTP, que é usado para medir o modeloVerifique o desempenho, a proporção de amostras previstas como positivas entre as amostras previstas corretamente.

Lembrar R ecall = TPTP + FN Recall = frac{TP}{TP+FN}RCEaeu=TP+FNTP, que é usado para medir o modeloDesempenho de pesquisa, a proporção de amostras que são realmente positivas entre as amostras que se prevê serem positivas.

Taxa Verdadeiramente Positiva (TPR):
TPR = TP / (TP + FN)
Taxa de falsos positivos (FPR):
FPR = FP / (FP + TN)
em:
TP: A amostra de ataque está corretamente identificada como um ataque;
TN: As amostras reais são corretamente identificadas como reais;
FP: Amostras reais são erroneamente identificadas como ataques;
FN: A amostra do ataque foi identificada incorretamente como real.

Referências: Aghajan, H., Augusto, JC, & Delgado, RLC (Eds.) (Se o link indicado no título não puder ser aberto, clique em:livro inteiro)

Aqui está meu script de cálculo TPR:

l1 = [0,1,1,1,0,0,0,1]
l2 = [0,1,0,1,0,1,0,0]

def accuracy(y_true, y_pred):
    # 正确预测数初始化一个简单计数器
    correct_counter = 0
    # 遍历y_true, y_pred中所有元素
    # zip函数接受多个元组,返回他们组成的列表
    for yt, yp in zip(y_true, y_pred):
        if yt == yp:
            # 如果预测标签与真实标签相同,则增加计数器
            correct_counter += 1
    # 返回正确率,正确标签数/总标签数
    return correct_counter / len(y_true)
    
def false_positive(y_true, y_pred):
    # 初始化假阳性样本计数器
    fp = 0
    # 遍历y_true,y_pred中所有元素
    for yt, yp in zip(y_true, y_pred):
        # 若真实标签为负类但预测标签为正类,计数器增加
        if yt == 0 and yp == 1:
            fp += 1
    return fp

def false_negative(y_true, y_pred):
    # 初始化假阴性样本计数器
    fn = 0
    # 遍历y_true,y_pred中所有元素
    for yt, yp in zip(y_true, y_pred):
        # 若真实标签为正类但预测标签为负类,计数器增加
        if yt == 1 and yp == 0:
            fn += 1
    return fn
    
def true_positive(y_true, y_pred):
    # 初始化真阳性样本计数器
    tp = 0
    # 遍历y_true,y_pred中所有元素
    for yt, yp in zip(y_true, y_pred):
        # 若真实标签为正类且预测标签也为正类,计数器增加
        if yt == 1 and yp == 1:
            tp += 1
    return tp

def true_negative(y_true, y_pred):
    # 初始化真阴性样本计数器
    tn = 0
    # 遍历y_true,y_pred中所有元素
    for yt, yp in zip(y_true, y_pred):
        # 若真实标签为负类且预测标签也为负类,计数器增加
        if yt == 0 and yp == 0:
            tn += 1
    # 返回真阴性样本数
    return tn
    
# 您可以尝试更好的精确度计算方式
def accuracy_v2(y_true, y_pred):
  # 真阳性样本数
  tp = true_positive(y_true, y_pred)
  # 假阳性样本数
  fp = false_positive(y_true, y_pred)
  # 假阴性样本数
  fn = false_negative(y_true, y_pred)
  # 真阴性样本数
  tn = true_negative(y_true, y_pred)
  # 准确率
  accuracy_score = (tp + tn) / (tp + tn + fp + fn)
  return accuracy_score
  
# F1-score的计算方法
def f1(y_true,y_pred):
    p = precision(y_true, y_pred)
    r = recall(y_true,y_pred)
    score = 2*p*r/(p+r)
    return score
    

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

Se a classificação for necessária, poderá ser necessário um limite. Sua relação com o valor previsto é a seguinte:

Predição = Probabilidade > Limiar Predição = Probabilidade > LimiarPeeucparaeuonãoão=Pparabebêeueuparae>Eoresooeue

Depois de aprender o AUC, outra métrica importante que você deve aprender é a perda de log. Para problemas de classificação binária, definimos perda de log como:

Log L oss = − alvo ∗ log ( p ) − ( 1 − alvo ) ∗ log ( 1 − p ) LogLoss = -alvo*log(p) - (1-alvo)*log(1-p)euogLosso=paraargeparaeuog(p)(1paraargepara)euog(1p)

Entre eles, o valor alvo é 0 ou 1, e o valor previsto é a probabilidade de a amostra pertencer à categoria 1. A perda de log penaliza previsões muito certas e muito erradas. Quanto menor a perda logarítmica, mais próxima a probabilidade prevista pelo modelo está do valor alvo.

Também podemos usar estes indicadores em problemas de classificação:

  • Precisão média macro: Calcule a precisão de todas as categorias separadamente e depois calcule a média
  • Precisão micromédia: Calcule a precisão de todas as categorias e depois calcule sua média ponderada.
  • Precisão ponderada : Calcule a precisão de todas as categorias e depois calcule sua média ponderada. A média ponderada é o produto dos pesos de cada categoria.

4. Linha de base geral

4.1 Calcule o número de amostras

# “word count” 的缩写,是一个用于计数的 Unix 命令。-l 只计算行数
!wc -l /kaggle/input/ffdv-sample-dataset/ffdv_phase1_sample/train_label.txt
!wc -l /kaggle/input/ffdv-sample-dataset/ffdv_phase1_sample/val_label.txt
  • 1
  • 2
  • 3

Precisamos apenas contar o número de linhas, que indica o número de amostras.

4.2 Criar objeto de vídeo

from IPython.display import Video
Video("/kaggle/input/ffdv-sample-dataset/ffdv_phase1_sample/valset/00882a2832edbcab1d3dfc4cc62cfbb9.mp4", embed=True)
  • 1
  • 2

Vídeo cria um objeto de vídeo e incorporar significa que quando definido como True, o player de vídeo será exibido diretamente na saída da célula do notebook.
Depois de executar a linha de base do kaggle, você verá resultados como este:
Insira a descrição da imagem aqui

4.3 Baixe as bibliotecas e conhecimentos complementares necessários

!pip install moviepy librosa matplotlib numpy timm
  • 1

Links de documentação para bibliotecas usadas:
filmepy
livrorosa(librosa é uma biblioteca de terceiros muito poderosa para processamento de sinais de fala em python. Nesta linha de base, usamos principalmente geração de espectrograma MEL e conversão de espectrograma)
matplotlib
entorpecido
Timão(Biblioteca de modelos de classificação de imagens, crie rapidamente vários modelos sota)

O que é SOTA? O nome completo da SOTA é State of the arts, que se refere ao melhor modelo na área. Pontuações SOTA muito altas em alguns conjuntos de dados de benchmark.

Modelo não ponta a ponta (pipeline): Em primeiro lugar, devemos entender o que é um fim. As duas extremidades referem-se à extremidade de entrada e à extremidade de saída. O processo tradicional de aprendizado de máquina consiste em vários módulos, que são independentes entre si. O resultado deste último módulo depende do nível do resultado anterior, afetando todo o resultado do treinamento.
Modelo ponta a ponta (ponta a ponta): Em primeiro lugar, você deve entender que a previsão é gerada do extremo de entrada até o extremo de saída. Este resultado de previsão terá um erro em comparação com o resultado real (deve ser lembrado). que a tarefa principal do aprendizado de máquina ainda éprever ), esse erro é propagado de volta para cada camada da rede neural, e os pesos e parâmetros do modelo são ajustados até que o modelo convirja ou os resultados que esperamos sejam obtidos. Se olharmos em termos de um sistema de controle, trata-se de um sistema de controle em malha fechada. (por exemplo, rede neural BP)
Sequência a sequência (seq2seq): Este é um procedimento geralde ponta a pontaMétodo de previsão de sequência, sua estrutura é um codificador e um decodificador. Se você usar o conjunto de dados de perguntas e respostas para codificar/decodificar, poderá obter um robô de perguntas e respostas.

A questão remonta à própria Linha de Base, o que é Linha de Base?A linha de base geralmente se refere a um modelo de linha de base simples e fácil de implementar.
No processo de ajuste de algoritmo e ajuste de parâmetros, a tarefa da Linha de Base é comparar-se consigo mesma para tornar o modelo cada vez melhor.

Benchmark também é um conceito importante, seu significado éReferências . Geralmente se refere a um método padronizado de avaliação e comparação do desempenho de algoritmos, modelos ou métodos, usado para medir as diferenças entre modelos.

Você pode vê-los frequentemente em sites de benchmarking de modelos.por exemploSi Nan

4.4 Definir configuração aleatória de sementes e CUDNN do pytorch

Quando executei a linha de base, ocorreu um erro de configuração CUDA. Use outro acelerador:

Insira a descrição da imagem aqui

import torch
# 设置pytorch的种子
torch.manual_seed(0)
# deterministic当设置为False时,cuDNN将允许一些操作的非确定性优化
torch.backends.cudnn.deterministic = False
# benchmark设置为true允许cuDNN在每个前向传播中自动寻找最适合当前配置的卷积算法,以提高性能。
torch.backends.cudnn.benchmark = True
# 导入必要的库,我们需要用到cv2,glob,os,PIL
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

import pandas as pd
import numpy as np
import cv2, glob, os
from PIL import Image
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

4.5 Pré-processamento de áudio e vídeo

Os parâmetros aceitos por generate_mel_spectrogram incluem o caminho do arquivo de vídeo, o número de filtros para dividir a frequência Mel, a frequência mais alta (controlando a faixa do espectro calculada) e o tamanho da imagem alvo.

import moviepy.editor as mp
import librosa
import numpy as np
import cv2

def generate_mel_spectrogram(video_path, n_mels=128, fmax=8000, target_size=(256, 256)):
    # 提取音频
    audio_path = 'extracted_audio.wav'
    # video_path 应该是之前定义的变量,包含了要处理的视频文件的路径。创建了一个 VideoFileClip 对象,存储在 video 变量中。
    video = mp.VideoFileClip(video_path)
    # video.audio 访问视频的音频轨道。write_audiofile() 方法将音频写入文件。verbose=False: 设置为False表示不在控制台输出处理进度。logger=None: 设置为None表示不使用日志记录器。实际上我们做这个预测没有这样的需求,也就不消耗占存。
    # 其默认参数:write_audiofile(self, filename, fps=None, nbytes=2, buffersize=2000, codec=None, bitrate=None, ffmpeg_params=None, write_logfile=False, verbose=True, logger='bar')
    video.audio.write_audiofile(audio_path, verbose=False, logger=None)

    # 加载音频文件,加载采样率
    y, sr = librosa.load(audio_path)

    # 生成MEL频谱图(梅尔频谱图,与之相对应的有mel倒频谱图)
    # 默认参数:librosa.feature.melspectrogram(y=None, sr=22050, S=None, n_fft=2048, hop_length=512, power=2.0, **kwargs)
    # 参数解释:y:音频时间序列,sr:采样率,n_mels 是指在计算梅尔频谱图时,将频谱图划分为多少个梅尔频率滤波器(Mel filters),其决定了最终生成的梅尔频谱图的分辨率,也可以理解为梅尔频谱图的高度。
    S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=n_mels)

    # 将频谱图转换为dB单位,S:输入功率,ref:作为参考,如果是标量,则振幅 abs(S) 相对于 ref: 10 * log10(S / ref) 进行缩放。此处np.max指的是将谱图中的最大值作为参考值,这也是一种常用的参考值取法
    S_dB = librosa.power_to_db(S, ref=np.max)

    # 归一化到0-255之间,NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化。
    S_dB_normalized = cv2.normalize(S_dB, None, 0, 255, cv2.NORM_MINMAX)
    
    # 将浮点数转换为无符号8位整型
    S_dB_normalized = S_dB_normalized.astype(np.uint8)

    # 缩放到目标大小256,256
    img_resized = cv2.resize(S_dB_normalized, target_size, interpolation=cv2.INTER_LINEAR)

    return img_resized

# 使用示例
video_path = '/kaggle/input/ffdv-sample-dataset/ffdv_phase1_sample/trainset/001b0680999447348bc9f89efce0f183.mp4'  # 替换为您的视频文件路径
mel_spectrogram_image = generate_mel_spectrogram(video_path)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

4.6 Criar pasta de dados de treinamento

!mkdir ffdv_phase1_sample
!mkdir ffdv_phase1_sample/trainset
!mkdir ffdv_phase1_sample/valset
  • 1
  • 2
  • 3

4.7 Gerar Espectrograma Mel

A quantidade de dados é muito grande, então não postarei uma foto aqui. Em vez disso, postarei um diagrama Mel em circunstâncias normais:
Insira a descrição da imagem aqui
Fonte da imagem:Universidade Simon Fraser
Se você ligar e ouvir, é um trecho de áudio que diminui gradativamente.

# 使用glob.glob函数查找/kaggle/input/ffdv-sample-dataset/ffdv_phase1_sample/trainset/目录下前400个.mp4视频文件的路径。
for video_path in glob.glob('/kaggle/input/ffdv-sample-dataset/ffdv_phase1_sample/trainset/*.mp4')[:400]:
    mel_spectrogram_image = generate_mel_spectrogram(video_path)
    cv2.imwrite('./ffdv_phase1_sample/trainset/' + video_path.split('/')[-1][:-4] + '.jpg', mel_spectrogram_image)
# a. 调用generate_mel_spectrogram(video_path)函数生成梅尔频谱图,并将其存储在mel_spectrogram_image变量中。b. 使用cv2.imwrite函数将梅尔频谱图保存为JPEG图像。图像被保存在./ffdv_phase1_sample/trainset/目录下,并使用与原始视频文件相同的名称(但扩展名改为.jpg)。
for video_path in glob.glob('/kaggle/input/ffdv-sample-dataset/ffdv_phase1_sample/valset/*.mp4'):
    mel_spectrogram_image = generate_mel_spectrogram(video_path)
    cv2.imwrite('./ffdv_phase1_sample/valset/' + video_path.split('/')[-1][:-4] + '.jpg', mel_spectrogram_image)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.8 Definir AverageMeter e ProgressMeter

A classe AverageMeter é usada para calcular e armazenar o valor médio e atual de uma variável.

  • nome: o nome da variável.
  • fmt: string de formato, usada para saída formatada.
  • reset(): redefine todas as estatísticas (val, avg, sum, count).
  • update(val, n=1): atualiza estatísticas, val é o valor atual e n é o peso do valor (geralmente o número de amostras).
  • str(): Retorna uma string formatada, incluindo o valor atual e o valor médio.
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__)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

A classe ProgressMeter é usada para gerar informações atuais do lote e indicadores estatísticos durante o processo de treinamento.

  • num_batches: número total de lotes.
  • metros: uma lista contendo objetos AverageMeter usados ​​para armazenar diferentes métricas.
  • prefixo: O prefixo da linha de saída.
  • print(lote): Imprime as informações do lote atual, incluindo o número do lote atual e o valor atual de cada indicador.
  • _get_batch_fmtstr(num_batches): Gere strings de formato em lote para garantir o alinhamento e formatação da saída.
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) + ']'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4.9 Aprendizado profundo e processo de avaliação de modelo (pontos-chave)

A função de validação avalia regularmente o desempenho do modelo no conjunto de validação durante o processo de treinamento e calcula e imprime a precisão Top-1.

def validate(val_loader, model, criterion):
    batch_time = AverageMeter('Time', ':6.3f')# 批处理时间
    losses = AverageMeter('Loss', ':.4e')# 损失
    top1 = AverageMeter('Acc@1', ':6.2f')# Top-1准确率
    progress = ProgressMeter(len(val_loader), batch_time, losses, top1)# 输出ProgressMeter

    # switch to evaluate mode,eval()为评估函数,关闭训练时使用的一些特定层(如 Dropout),并启用 Batch Normalization 层的运行统计。
    model.eval()

    with torch.no_grad():# 定时设置requires_grad为False,防止梯度计算并节省内存。
        end = time.time()
        for i, (input, target) in enumerate(val_loader):
            input = input.cuda()# 将输入数据和目标数据转移到GPU计算
            target = target.cuda()

            # compute output
            output = model(input)
            loss = criterion(output, target)# 计算训练损失

            # measure accuracy and record loss,acc百分比显示
            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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

A função de previsão é usada para fazer inferências no conjunto de testes e suporta o uso de aumento de tempo de teste (TTA) para melhorar a estabilidade das previsões do modelo, fazendo múltiplas previsões e calculando a média.

def predict(test_loader, model, tta=10):
    # switch to evaluate mode
    model.eval()
    # TTA(Test Time Augmentation)
    test_pred_tta = None
    for _ in range(tta):# 执行 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)# 对模型输出进行 softmax 归一化处理,以获得类别概率。
                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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

A função train é responsável por treinar o modelo, atualizar os parâmetros do modelo calculando a função de perda e precisão e realizar etapas de retropropagação e otimização.

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
        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))# 更新 top1 计量器,记录当前批次的准确率。

        # compute gradient and do SGD step
        optimizer.zero_grad() # 清除之前累积的梯度。
        loss.backward()# 计算损失相对于模型参数的梯度
        optimizer.step()# 根据 backward() 计算的梯度更新模型参数。

        # measure elapsed time
        batch_time.update(time.time() - end)# 更新 batch_time 计量器,记录当前批次的处理时间。
        end = time.time()

        if i % 100 == 0:
            progress.pr2int(i)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

4.10 Carregar rótulos para conjuntos de dados de treinamento e validação

train_label = pd.read_csv("/kaggle/input/ffdv-sample-dataset/ffdv_phase1_sample/train_label.txt")
val_label = pd.read_csv("/kaggle/input/ffdv-sample-dataset/ffdv_phase1_sample/val_label.txt")

train_label['path'] = '/kaggle/working/ffdv_phase1_sample/trainset/' + train_label['video_name'].apply(lambda x: x[:-4] + '.jpg')
val_label['path'] = '/kaggle/working/ffdv_phase1_sample/valset/' + val_label['video_name'].apply(lambda x: x[:-4] + '.jpg')

train_label = train_label[train_label['path'].apply(os.path.exists)]
val_label = val_label[val_label['path'].apply(os.path.exists)]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.11 Carregando imagens e conversão de imagens

transform deixa um parâmetro para aprimoramento de dados subsequente e o padrão é Nenhum.
A imagem é convertida para o modo RGB.
Os rótulos são retornados como torch.Tensor.

class FFDIDataset(Dataset):
    def __init__(self, img_path, img_label, transform=None):
        self.img_path = img_path
        self.img_label = img_label
        
        if transform is not None:
            self.transform = transform
        else:
            self.transform = None
    
    def __getitem__(self, index):
        img = Image.open(self.img_path[index]).convert('RGB')
        
        if self.transform is not None:
            img = self.transform(img)
        
        return img, torch.from_numpy(np.array(self.img_label[index]))
    
    def __len__(self):
        return len(self.img_path)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4.12 Configure o processo de treinamento (pontos-chave)

Faz referência à classe FFDID definida acima.

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
)
# 重点:这里调用timm提供的resnet18模型,因为分类为0/1(真视频/假视频),可以在后续改进,比如换用更深的网络ResNet-34、ResNet-50或是其他变体
model = timm.create_model('resnet18', pretrained=True, num_classes=2)
model = model.cuda()

# 交叉熵损失,针对多类别
criterion = nn.CrossEntropyLoss().cuda()
# Adam优化器,学习率设置为0.003。
optimizer = torch.optim.Adam(model.parameters(), 0.003)
# 每4个epoch将学习率按0.85的因子进行调整。
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.85)
# 初始化最优acc
best_acc = 0.0
for epoch in range(10):
    scheduler.step()
    print('Epoch: ', epoch)
	# 调用train函数
    train(train_loader, model, criterion, optimizer, epoch)
    # 调用validate函数
    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')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

Saída:

Epoch:  0
[ 0/10]	Time  6.482 ( 6.482)	Loss 7.1626e-01 (7.1626e-01)	Acc@1  35.00 ( 35.00)
 * Acc@1 64.000
Epoch:  1
[ 0/10]	Time  0.819 ( 0.819)	Loss 4.6079e-01 (4.6079e-01)	Acc@1  80.00 ( 80.00)
 * Acc@1 75.500
Epoch:  2
[ 0/10]	Time  0.914 ( 0.914)	Loss 1.4983e-01 (1.4983e-01)	Acc@1  97.50 ( 97.50)
 * Acc@1 88.500
Epoch:  3
[ 0/10]	Time  0.884 ( 0.884)	Loss 2.4681e-01 (2.4681e-01)	Acc@1  87.50 ( 87.50)
 * Acc@1 84.000
Epoch:  4
[ 0/10]	Time  0.854 ( 0.854)	Loss 5.3736e-02 (5.3736e-02)	Acc@1 100.00 (100.00)
 * Acc@1 90.500
Epoch:  5
[ 0/10]	Time  0.849 ( 0.849)	Loss 5.9881e-02 (5.9881e-02)	Acc@1  97.50 ( 97.50)
 * Acc@1 89.500
Epoch:  6
[ 0/10]	Time  0.715 ( 0.715)	Loss 1.6215e-01 (1.6215e-01)	Acc@1  92.50 ( 92.50)
 * Acc@1 65.000
Epoch:  7
[ 0/10]	Time  0.652 ( 0.652)	Loss 5.3892e-01 (5.3892e-01)	Acc@1  80.00 ( 80.00)
 * Acc@1 78.500
Epoch:  8
[ 0/10]	Time  0.847 ( 0.847)	Loss 6.6098e-02 (6.6098e-02)	Acc@1  97.50 ( 97.50)
 * Acc@1 81.000
Epoch:  9
[ 0/10]	Time  0.844 ( 0.844)	Loss 9.4254e-02 (9.4254e-02)	Acc@1  97.50 ( 97.50)
 * Acc@1 81.500
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

4.12.1 Ideias para melhorar o modelo resnet18

Redes mais profundas: se você precisar de maior desempenho e recursos de extração de recursos mais complexos, poderá considerar o uso de redes mais profundas, como ResNet-34, ResNet-50 ou variantes ResNet ainda maiores (como ResNet-101 ou ResNet-152).

Outros modelos pré-treinados: Além da série ResNet, existem muitos outros modelos pré-treinados para você escolher, como:

EfficientNet: possui excelente desempenho e eficiência de parâmetros.
DenseNet: A estrutura de rede densamente conectada ajuda a utilizar melhor os recursos.
Série VGG: Arquitetura simples e clássica, adequada para uso sob restrições de recursos.
Modelo personalizado: Dependendo das características específicas do conjunto de dados e dos requisitos da tarefa, você também pode considerar projetar e treinar uma arquitetura de modelo personalizada, o que pode exigir mais depuração e experimentação.

Aprendizagem em conjunto: considere o uso de métodos de aprendizagem em conjunto, como bagging ou boosting, para combinar as previsões de vários modelos e melhorar ainda mais o desempenho e a estabilidade.

Ajuste de hiperparâmetros: além da seleção do modelo, o desempenho do modelo também pode ser otimizado ajustando a taxa de aprendizado, o tamanho do lote, a seleção do otimizador e as estratégias de aumento de dados.

4.12.2 Melhoria na seleção da função de perda

Considere aplicar Dice Loss para melhorar a função de perda no futuro. Dice Loss mede a semelhança entre o resultado da previsão e a máscara de destino e é melhor para tarefas de classificação binária com limites óbvios. É uma função de perda com melhor desempenho na previsão em nível de pixel.

Fique de olho também na Perda Focal. Projetado especificamente para resolver o problema de desequilíbrio de classes, ele pode melhorar ainda mais o desempenho do modelo em categorias minoritárias, reduzindo o peso de amostras fáceis de classificar para focar em amostras difíceis.

4.12.3 Melhorias na seleção do otimizador

RAdam é uma melhoria no Adam que melhora a estabilidade e o desempenho ajustando dinamicamente a correção da taxa de aprendizagem.

AdamW é uma variante do Adam que introduz redução de peso para resolver os problemas de desempenho que Adam pode introduzir em alguns casos, especialmente quando o número de parâmetros do modelo é grande.

AdamW é uma variante do Adam que introduz redução de peso para resolver os problemas de desempenho que Adam pode introduzir em alguns casos, especialmente quando o número de parâmetros do modelo é grande.

4.13 Modelo de avaliação

# 用模型 (model) 对验证数据集 (val_loader) 进行预测。这部分假设 [:, 1] 给出了类别1的概率。
val_pred = predict(val_loader, model, 1)[:, 1]
# 赋值,预测的概率(或者预测值)赋给了 val_label 数据框中名为 "y_pred" 的列
val_label["y_pred"] = val_pred
  • 1
  • 2
  • 3
  • 4

4.14 Integrando o conjunto de dados final

submit = pd.read_csv("/kaggle/input/multi-ffdv/prediction.txt.csv")
# 使用 merge 函数将提交文件 (submit) 中的数据与验证数据集标签 (val_label) 中的 video_name 和 y_pred 列合并
merged_df = submit.merge(val_label[['video_name', 'y_pred']], on='video_name', suffixes=('', '_df2'), how='left', )
# 将合并后的数据中 y_pred_df2 列(从验证集中获取的预测结果)的值填充到 y_pred 列中
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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

5. Conclusão

Não leva 10 minutos para terminar a execução da Linha de Base. Geralmente, ela ficará parada em 4,7 para gerar o espectrograma Mel. Mas são necessárias 5 horas de exploração paciente. Aqui está um resumo silencioso dos principais processos:

  • Calcule o número de amostras
  • Crie objetos de áudio e vídeo
  • Importar conjunto de dados de treinamento
  • Importe dados de áudio e vídeo
  • Definir semente aleatória/CUDNN
  • Pré-processamento de áudio e vídeo
  • Gerar espectrograma mel
  • Definir métodos de conjunto de validação, conjunto de previsão e conjunto de treinamento
  • conversão de imagem
  • Use o treinamento SOTA, modelo de avaliação de otimização fornecido pela timm

6. Expandir

6.1 Definição de tarefa de aprendizagem profunda

A definição da tarefa de aprendizagem profunda pode, na verdade, ser resumida como "retropropagação", porque seu núcleo é usar o algoritmo de retropropagação para ajustar os parâmetros do modelo para minimizar a função de perda definida.

É muito adequado usar o aprendizado profundo para lidar com essas tarefas de áudio e vídeo. Acho que o primeiro é a enorme quantidade de dados de áudio e vídeo e a necessidade de classificação complexa desses dados.O mecanismo de aprendizagem profunda determina que requer uma enorme quantidade de dados e, de fato, um ponto muito importante é que o próprio deepfake requerClassificaçãoO pensamento é essencialmente uma tarefa de classificação, e o aprendizado profundo tem grandes vantagens em grandes volumes de dados e classificação refinada. Um exemplo familiar é a rede de geração adversária GAN.

Em segundo lugar, dados estatisticamente semelhantes são criados em grandes quantidades e podem aprender a distribuição de grandes quantidades de dados. Também pode fazer a tarefa inversa.

6.2 A relação entre AIGC e Deepfake

Insira a descrição da imagem aqui

AIGC deve incluir Deepfake. Do ponto de vista do desenvolvimento, o desenvolvimento e a dimensionalidade da tecnologia Deepfake certamente aumentarão com o poder de processamento do AIGC. Seremos confrontados com um vasto mar de dados reais e falsos. esses vídeos são. Se puderem economizar A energia dos criadores de histórias é, sem dúvida, benéfica para o desenvolvimento do entretenimento literário e artístico; se for usada para enganar, em breve enfrentará desafios éticos e morais;

Um dos meus pensamentos é que, se combinado com a atual mania de dramas curtos, Deepfake pode permitir ao público obter experiência de entretenimento audiovisual de custo ultrabaixo, mas também desafia a indústria tradicional de cinema e televisão e até mesmo que tipo de atores e estimulação audiovisual que os olhos humanos precisam. Este é um tema discutível.