1.导入必要的库和设置PyTorch选项
import torch
torch.manual_seed(0)
torch.backends.cudnn.deterministic = False
torch.backends.cudnn.benchmark = True
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
from PIL import Image
from tqdm import tqdm_notebook
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']
这段代码的主要功能是准备环境、导入库、读取数据以及处理数据路径,为后续的深度学习模型训练和评估做准备。
torch.backends.cudnn.deterministic = False
:设置CuDNN库的确定性模式为False,允许更快的卷积操作。
torch.backends.cudnn.benchmark = True
:启用CuDNN基准模式,这可以让CuDNN在运行时为卷积操作选择最佳算法,从而加速训练过程。
2.AverageMeter 类和ProgressMeter 类
AverageMeter 类用于计算和存储一个值的当前值、总和、计数和平均值,常用于训练过程中对某些度量(如损失或准确率)进行跟踪。
ProgressMeter 类用于显示训练过程中的进度信息,包含多个 AverageMeter
对象。
这两个类通常用于深度学习训练循环中,帮助监控训练的进展和性能。
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) + ']'
3.模型训练与评估
def validate(val_loader, model, criterion):
# 创建AverageMeter实例来记录时间、损失和准确率
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)
# 切换到评估模式
model.eval()
with torch.no_grad():
end = time.time()
for i, (input, target) in tqdm_notebook(enumerate(val_loader), total=len(val_loader)):
input = input.cuda()
target = target.cuda()
# 计算输出
output = model(input)
loss = criterion(output, target)
# 测量准确率并记录损失
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))
# 计算经过的时间
batch_time.update(time.time() - end)
end = time.time()
# 打印最终的准确率
print(' * Acc@1 {top1.avg:.3f}'
.format(top1=top1))
return top1
def predict(test_loader, model, tta=10):
# 切换到评估模式
model.eval()
test_pred_tta = None
for _ in range(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()
# 计算输出
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):
# 创建AverageMeter实例来记录时间、损失和准确率
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):
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))
# 计算梯度并执行SGD优化步骤
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 计算经过的时间
batch_time.update(time.time() - end)
end = time.time()
if i % 100 == 0:
progress.pr2int(i)
-
validate函数:
- 创建三个
AverageMeter
实例来记录批处理时间、损失和Top-1准确率。 model.eval()
切换模型到评估模式。- 使用
torch.no_grad()
禁用梯度计算,以加速计算并减少内存消耗。 - 遍历验证数据加载器,计算模型输出、损失和准确率,并更新计量器。
- 打印最终的Top-1准确率。
- 创建三个
-
predict函数:
tta
表示测试时增强的次数(Test-Time Augmentation)。- 切换模型到评估模式。
- 遍历测试数据加载器,计算模型输出,使用Softmax将输出转换为概率分布。
- 将所有增强的预测结果累加,最后返回所有增强的预测结果的总和。
-
train函数:
- 创建三个
AverageMeter
实例来记录批处理时间、损失和Top-1准确率。 model.train()
切换模型到训练模式。- 遍历训练数据加载器,计算模型输出、损失和准确率,并更新计量器。
- 计算梯度并执行优化步骤(SGD)。
- 每100个批次打印一次当前进度。
- 创建三个
这段代码主要用于训练和评估深度学习模型,包含了计算准确率、损失以及记录训练和评估过程中的各种度量值。
4.FFDIDataset
类
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)
这个类继承自 torch.utils.data.Dataset
,用于自定义数据集的加载和处理。它将图像和对应的标签加载到模型中,并支持数据转换。
__init__(self, img_path, img_label, transform=None)
:
- 初始化方法,接受图像路径列表
img_path
,图像标签列表img_label
,以及可选的数据转换transform
。 - 如果提供了
transform
,则将其存储在self.transform
中,否则将self.transform
设置为None
。
__getitem__(self, index)
:
- 获取指定索引
index
处的图像和标签。 - 使用
PIL.Image.open
打开图像并转换为 RGB 模式。 - 如果提供了
transform
,则对图像进行转换。 - 将图像标签转换为 NumPy 数组,再转换为
torch
张量。 - 返回转换后的图像和标签。
__len__(self)
:
- 返回数据集中图像的数量,即
img_path
列表的长度。
5.模型加载
在代码中已加入注释
import timm
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
# 创建ResNet18模型,并加载预训练权重,设置输出类别为2
model = timm.create_model('resnet18', pretrained=True, num_classes=2)
model = model.cuda()
# 创建训练数据加载器
train_loader = DataLoader(
FFDIDataset(
train_label['path'].head(1000), # 使用前1000个训练图像路径
train_label['target'].head(1000), # 使用前1000个训练标签
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, # 每个批次40张图像
shuffle=True, # 打乱数据
num_workers=4, # 使用4个子进程加载数据
pin_memory=True # 将数据加载到固定内存中(更快的数据传输到GPU)
)
# 创建验证数据加载器
val_loader = DataLoader(
FFDIDataset(
val_label['path'].head(1000), # 使用前1000个验证图像路径
val_label['target'].head(1000), # 使用前1000个验证标签
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, # 每个批次40张图像
shuffle=False, # 不打乱数据
num_workers=4, # 使用4个子进程加载数据
pin_memory=True # 将数据加载到固定内存中
)
# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss().cuda()
optimizer = torch.optim.Adam(model.parameters(), 0.005)
# 定义学习率调度器
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.85)
best_acc = 0.0
# 训练和验证循环
for epoch in range(2):
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')
6.模型验证
# 创建测试数据加载器
test_loader = torch.utils.data.DataLoader(
FFDIDataset(
val_label['path'], # 使用所有验证图像路径
val_label['target'], # 使用所有验证标签
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, # 每个批次40张图像
shuffle=False, # 不打乱数据
num_workers=4, # 使用4个子进程加载数据
pin_memory=True # 将数据加载到固定内存中
)
# 使用模型进行预测,并将预测结果添加到验证标签数据框中
val_label['y_pred'] = predict(test_loader, model, 1)[:, 1]
# 将图像名称和预测结果保存到CSV文件中
val_label[['img_name', 'y_pred']].to_csv('submit.csv', index=None)
附赛事详细链接:
【学习手册】 https://datawhaler.feishu.cn/wiki/WwqqwOQ1wikEG0ksOUcc4SnEnPf
【打卡链接】 https://datawhaler.feishu.cn/share/base/form/shrcnm0mqlp3kHsy2oYmXGqks3e
【Q&A手册--QA文档的权限对所有人开放哈 大家可随时查看和编辑】 https://datawhaler.feishu.cn/wiki/YcRLw35DhiYqBqkf4zwc35O8nOg