baseline的解读
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']
-
设置PyTorch环境:
- 通过
torch.manual_seed(0)设置随机种子,以确保结果的可重复性。 - 设置
torch.backends.cudnn.deterministic = False和torch.backends.cudnn.benchmark = True以优化CUDA操作的执行速度。deterministic=False允许CUDA操作非确定性执行(即可能更快但结果可能略有不同),而benchmark=True让PyTorch在开始时为当前配置寻找最优的卷积算法。
- 通过
-
导入必要的库:
- 导入PyTorch相关的模块,如
torchvision.models(预训练模型)、torchvision.transforms(图像预处理)、torchvision.datasets(标准数据集)、torch.nn和torch.nn.functional(神经网络模块和功能)、torch.optim(优化器)。 - 导入
timm,这是一个流行的库,提供了更多的预训练模型和高效的图像模型加载工具。 - 导入
pandas用于数据处理,numpy用于数学运算,cv2(OpenCV)和PIL.Image用于图像操作,time用于计时,tqdm_notebook用于显示进度条(尽管这里代码没有直接使用tqdm_notebook,但可能是为了后续步骤准备的)。
- 导入PyTorch相关的模块,如
-
加载标签数据:
- 使用
pandas的read_csv函数加载训练和验证集的标签文件。这些文件(trainset_label.txt和valset_label.txt)很可能包含了图像的标签(例如,是否为Deepfake)和图像的名称。 - 在
train_label和val_label数据框中添加'path'列,通过拼接字符串来生成图像的完整路径。这里假设图像文件位于指定的文件夹中,文件名与标签文件中的'img_name'列相对应。
- 使用
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) + ']'
ProgressMeter和AverageMeter类中,ProgressMeter类用于跟踪和打印训练过程中的进度,而AverageMeter类用于计算和存储平均值和其他统计信息。关于ProgressMeter类中_get_batch_fmtstr方法的代码精度,这里有一些细节需要注意:
在_get_batch_fmtstr方法中,您试图为批次编号设置一个格式化字符串,以确保无论批次总数如何,批次编号在打印时都保持一致的宽度。这是通过计算批次总数的位数来实现的,并使用这个位数来设置格式化字符串。然而,有一个小错误在len(str(num_batches // 1))这一行中,num_batches // 1实际上总是等于num_batches,因为//是整数除法,并且除以1不会改变数值。
更正这个细节后,我们可以直接使用len(str(num_batches))来获取批次总数的位数。但是,这仍然有一个潜在的问题:如果num_batches是一个非常大的数字,这种方法可能会生成一个很长的格式化字符串,这在某些情况下可能不是所期望的。通常,我们可能希望限制这个格式化字符串的长度,比如最多保留几位数。
不过,对于大多数实际应用来说,直接使用len(str(num_batches))来获取位数是足够的,因为它确保了无论num_batches的值如何,批次编号都会以相同的宽度打印出来。
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 tqdm_notebook(enumerate(val_loader), total=len(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 tqdm_notebook(enumerate(test_loader), total=len(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
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()
validate 和 predict,它们通常用于深度学习模型的验证和预测阶段。这两个函数都依赖于PyTorch框架,并且利用了自定义的AverageMeter和ProgressMeter类来跟踪和记录性能指标。下面是对这两个函数的详细解释:
validate 函数
该函数用于在验证集上评估模型的性能。它接收验证集加载器(val_loader)、模型(model)和损失函数(criterion)作为输入,并返回在验证集上的Top-1准确率(Acc@1)。
- 初始化:首先,初始化用于记录时间、损失和准确率的
AverageMeter对象,以及一个ProgressMeter对象来跟踪进度。 - 模型评估模式:将模型设置为评估模式,这通常会影响特定层(如Dropout和BatchNorm)的行为。
- 无梯度计算:使用
torch.no_grad()上下文管理器,确保在评估过程中不计算梯度,以节省内存和加速计算。 - 遍历验证集:使用
tqdm_notebook(尽管代码中未显示导入,但假设已正确导入)来遍历验证集,这提供了一个带进度条的枚举器。对于每个批次,将数据移至GPU(如果可用),计算模型输出、损失和准确率,并更新相应的AverageMeter对象。 - 性能记录:在遍历结束后,打印Top-1准确率的平均值,并返回该平均值。
predict 函数
该函数用于在测试集上进行预测,支持测试时增强(Test Time Augmentation, TTA)以提高预测性能。它接收测试集加载器(test_loader)、模型(model)和TTA次数(tta)作为输入,并返回测试集上所有样本的预测结果的平均值(如果启用了TTA)。
-
模型评估模式:与
validate函数类似,首先将模型设置为评估模式。 -
测试时增强:对于指定的TTA次数,重复以下步骤:
- 初始化一个空列表
test_pred来存储当前TTA迭代的预测结果。 - 遍历测试集,对于每个批次,将数据移至GPU,计算模型输出,并通过softmax函数将其转换为概率分布。然后,将输出转换为NumPy数组并存储在
test_pred列表中。 - 如果这是第一次TTA迭代,
test_pred_tta被初始化为test_pred的副本;否则,将当前迭代的预测结果累加到test_pred_tta上。
- 初始化一个空列表
-
返回结果:最后,返回所有TTA迭代预测结果的平均值(通过累加和除以TTA次数隐式实现)。