Datawhale AI 夏令营 CV 方向 Baseline 解读

 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)
  1. validate函数

    • 创建三个 AverageMeter 实例来记录批处理时间、损失和Top-1准确率。
    • model.eval() 切换模型到评估模式。
    • 使用 torch.no_grad() 禁用梯度计算,以加速计算并减少内存消耗。
    • 遍历验证数据加载器,计算模型输出、损失和准确率,并更新计量器。
    • 打印最终的Top-1准确率。
  2. predict函数

    • tta 表示测试时增强的次数(Test-Time Augmentation)。
    • 切换模型到评估模式。
    • 遍历测试数据加载器,计算模型输出,使用Softmax将输出转换为概率分布。
    • 将所有增强的预测结果累加,最后返回所有增强的预测结果的总和。
  3. 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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值