实现一个基于深度学习的Deepfake检测模型
在官方的Task2的学习中,提供了很详细的入门深度学习的教程,链接在这Task2:从baseline入门深度学习 - 飞书云文档 (feishu.cn)
可以点击进行学习,下面是是我做的实验,也算是对Task1中提到的直接使用视频进行实验的一个补充吧。可以扩展思路!!!
在本文中,将探讨如何使用深度学习技术来开发一个用于检测Deepfake的模型。我将从头开始介绍每个步骤,帮助你了解如何处理数据、构建模型、训练模型以及评估其性能。
1. 数据集准备
首先,需要准备一个包含Deepfake视频和对应标签的数据集。每个视频样本都有一个标签,指示其是否是Deepfake。将使用Python和几个关键的库来处理视频和标签数据。(个人觉得对于深度学习来说,特别是新手,手写Dataset是很有帮助的,在今后的学习中也更加的重要)
import os
import pandas as pd
# 定义一个自定义的数据集类
class DeepfakeDataset(Dataset):
def __init__(self, csv_file, root_dir, transform=None):
"""
初始化数据集类
Parameters:
- csv_file (str): 包含视频文件名和标签的CSV文件路径
- root_dir (str): 视频文件的根目录
- transform (callable, optional): 可选的数据变换操作
"""
self.labels = pd.read_csv(csv_file) # 从CSV文件中读取标签数据
self.root_dir = root_dir # 视频文件的根目录
self.transform = transform # 数据变换操作,用于图像预处理等
def __len__(self):
"""
返回数据集的样本数量
"""
return len(self.labels)
def __getitem__(self, idx):
"""
根据索引获取单个样本的视频帧和标签
Parameters:
- idx (int): 样本的索引
Returns:
- frames (Tensor): 视频帧的张量数据,形状为 (3, 16, 112, 112)
- label (int): 样本的标签,1表示Deepfake,0表示非Deepfake
"""
video_name = self.labels.iloc[idx, 0] # 获取视频文件名
label = self.labels.iloc[idx, 1] # 获取标签(1表示Deepfake,0表示非Deepfake)
video_path = os.path.join(self.root_dir, video_name) # 构建完整的视频文件路径
# 调试信息:打印正在读取的视频路径
print(f"Reading video: {video_path}")
# 使用OpenCV读取视频文件
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print(f"Failed to read video: {video_path}")
# 返回一个空的tensor和标签,以避免程序崩溃
return torch.zeros((3, 16, 112, 112)), label
frames = []
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 获取视频总帧数
frame_interval = max(1, total_frames // 16) # 计算帧采样间隔,以便从视频中提取16帧
# 逐帧读取视频,并应用数据变换(如果有)
for i in range(0, total_frames, frame_interval):
cap.set(cv2.CAP_PROP_POS_FRAMES, i)
ret, frame = cap.read()
if not ret:
break
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 转换成RGB格式
if self.transform:
frame = self.transform(frame) # 应用数据变换
frames.append(frame)
if len(frames) == 16: # 收集到16帧后停止
break
cap.release() # 释放视频捕获资源
# 如果收集到的帧数少于16帧,则进行复制填充
if len(frames) < 16:
for _ in range(16 - len(frames)):
frames.append(frames[-1])
# 将帧列表堆叠成一个4D张量,形状为(16, 3, 112, 112)
frames = torch.stack(frames)
# 调整张量的维度顺序,以适应C3D模型的输入要求,形状变为(3, 16, 112, 112)
frames = frames.permute(1, 0, 2, 3)
return frames, label # 返回视频帧张量和标签
在这个部分,通过一个自定义的DeepfakeDataset
类来处理数据集。它可以从CSV文件中读取视频文件名和标签信息,并根据需要对视频帧进行预处理。例如,可以在这里应用图像变换,如大小调整、标准化等操作,以便将数据准备好用于模型训练。
2. 深度学习模型选择与构建
为了检测Deepfake,选择了C3D模型,当然你可以选择其他模型。这是一种经典的3D卷积神经网络,特别适用于处理视频数据。你可以像搭积木一样搭建它。
import torch
import torch.nn as nn
# 定义C3D模型
class C3D(nn.Module):
def __init__(self, num_classes=2):
super(C3D, self).__init__()
# 定义特征提取层
self.features = nn.Sequential(
nn.Conv3d(3, 64, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool3d(kernel_size=(1, 2, 2), stride=(1, 2, 2)),
nn.Conv3d(64, 128, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2)),
nn.Conv3d(128, 256, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.Conv3d(256, 256, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2)),
nn.Conv3d(256, 512, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.Conv3d(512, 512, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2)),
nn.Conv3d(512, 512, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.Conv3d(512, 512, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))
)
# 定义分类器层
self.classifier = nn.Sequential(
nn.Linear(512 * 2 * 7 * 7, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, num_classes)
)
def forward(self, x):
x = self.features(x) # 特征提取
x = x.view(x.size(0), -1) # 展平特征图
x = self.classifier(x) # 分类
return x
3. 模型训练与性能评估
现在,将介绍如何训练Deepfake检测模型,并在每个epoch后评估模型在验证集上的性能。将使用BCEWithLogitsLoss作为损失函数,Adam优化器进行参数优化,并使用相应的评估指标。
import torch.optim as optim
from sklearn.metrics import roc_auc_score
import pandas as pd
# 设置训练参数和数据加载器
train_loader = DataLoader(train_dataset, batch_size=4, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=4, shuffle=False)
# 初始化模型、损失函数和优化器
model = C3D(num_classes=1)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
criterion = nn.BCEWithLogitsLoss() # 二分类问题使用BCEWithLogitsLoss
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模型
num_epochs = 10
best_auc = 0.0
for epoch in range(num_epochs):
model.train() # 设置模型为训练模式
running_loss = 0.0
for inputs, labels in train_loader:
inputs, labels = inputs.to(device), labels.to(device).float().unsqueeze(1)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {running_loss / len(train_loader):.4f}')
# 在验证集上评估模型性能
model.eval() # 设置模型为评估模式
all_labels = []
all_outputs = []
with torch.no_grad():
for inputs, labels in val_loader:
inputs, labels = inputs.to(device), labels.to(device).float().unsqueeze(1)
outputs = model(inputs)
all_labels.extend(labels.cpu().numpy())
all_outputs.extend(torch.sigmoid(outputs).cpu().numpy())
# 评估模型性能
auc = roc_auc_score(all_labels, all_outputs)
print(f'Validation AUC: {auc:.4f}')
# 保存最佳模型
if auc > best_auc:
best_auc = auc
torch.save(model.state_dict(), 'best_model.pth')
# 保存预测结果到 CSV 文件
predictions = pd.DataFrame({'video_name': val_dataset.labels.iloc[:, 0], 'score': all_outputs})
predictions.to_csv('prediction.csv', index=False)
print("Prediction results saved to 'prediction.csv'.")