Vision Transformer(ViT)模型及其在图像分类任务中的应用

一、实验目的

1.简要介绍 Vision Transformer(ViT)模型及其在图像分类任务中的应用。

         基本概念

  • Vision Transformer (ViT) 是一种基于Transformer架构的模型,最初在自然语言处理领域获得成功,后被引入到计算机视觉领域。
  • 核心思想:ViT将图像分割成一系列小块(称为patches),这些小块被处理成序列数据,类似于在NLP中处理单词序列。
  • 自注意力机制:ViT通过自注意力机制学习图像中不同部分之间的关系,这与传统的CNN使用卷积核在空间上聚焦局部区域形成鲜明对比。

与CNN的演化

  • 从CNN到ViT:传统的卷积神经网络(CNN)依赖于局部卷积操作和池化来处理图像,这导致它们在处理图像的全局信息时存在局限性。
  • 全局信息处理:ViT通过自注意力机制克服了这一限制,能够在整个图像序列上建立长距离依赖关系。
  • 高效性:与CNN相比,ViT在处理高分辨率图像时更为高效,因为它避免了逐层卷积操作中的重复计算

2. 阐述实验目的和重要性。

         Vision Transformer在图像分类任务中的应用标志着从局部聚焦的CNN到全局信息处理的转变,这在图像理解和分析中打开了新的可能性。由于其独特的结构和处理方式,ViT在处理复杂和多样化的图像数据方面显示出显著的优势。

二、实验设计:

1. 实验的整体框架和步骤

  • 步骤一:数据准备
    • 选择猫狗图像数据集:使用专门的猫狗图像数据集,如Kaggle上的猫狗分类挑战数据集。
    • 数据预处理:包括调整图像大小、归一化等。
  • 步骤二:模型设计与实现
    • 设计Vision Transformer模型:根据ViT的架构设置模型参数,调整以适应猫狗图像特性。
    • 编写实验代码:实现模型训练、验证和测试的过程。
  • 步骤三:模型训练
    • 在训练集上训练模型:调整超参数以优化猫狗图像分类的性能。
    • 在验证集上进行性能评估。
  • 步骤四:模型测试与评估
    • 在测试集上评估模型性能。
    • 分析结果:分析模型在猫狗分类任务中的表现。

2. Vision Transformer模型的结构和原理

  • 针对猫狗分类调整模型结构
    • 输入处理:根据猫狗图像的特点调整patch大小。
    • 位置嵌入和Transformer编码器:同标准ViT模型。
    • 分类头:设计为二分类输出。
  • 模型原理
    • 自注意力机制:学习猫和狗图像中关键特征的相关性。
    • 层序连接和分类头:适用于猫狗分类任务。

3. 图像数据的预处理与划分

  • 预处理
    • 调整大小:统一图像尺寸,如将所有图像调整为256x256像素。
    • 归一化:将像素值归一化到0-1范围。
    • 数据增强:应用如随机裁剪、旋转等技术增加数据多样性。
  • 数据划分
    • 训练集:选取数据集的大部分(例如70%)用于模型训练。
    • 验证集:约15%的数据用于模型的性能调优。
    • 测试集:剩余的15%用于最终的性能评估。

三、实验过程:

1. 软件和硬件环境

  • 软件环境
    • 编程语言:Python。
    • 深度学习框架:PyTorc

2. 模型训练过程

  • 超参数的选择
    • 学习率:开始时可以设置较大的学习率(例如0.001),并在训练过程中逐步降低。
    • 批处理大小:根据GPU内存容量调整(例如32或64)。
    • Epoch数量:设置足够多的epoch以确保充分训练(例如100个epoch)。
  • 优化器的设置
    • 优化器选择:通常使用Adam优化器,因其在深度学习模型中表现良好。
    • 正则化技术:如权重衰减(L2正则化)以防止过拟合。

3. 训练和调优策略

  • 损失函数的选择
    • 对于猫狗二分类任务,使用二元交叉熵损失(Binary Cross-Entropy Loss)。
  • 训练策略
    • 早期停止:监控验证集的损失,如果在连续几个epoch中没有明显改善,则提前终止训练。
    • 学习率调整:使用学习率衰减策略,如在验证集损失停止改善时降低学习率。
    • 数据增强:在训练过程中应用数据增强技术,增加模型的泛化能力。
  • 调优过程
    • 超参数调优:通过实验不同的超参数组合来找到最佳配置。
    • 性能监控:定期检查模型在训练集和验证集上的性能,确保模型正在学习并且没有过拟合。
    • 结果记录:记录不同超参数设置下的训练结果,以便进行比较和分析。

四、实验结果:

1. 验证集上的模型表现

Loss-Acc图:

ROC曲线和AUC值:

  • 性能指标
    • 准确率:表示模型正确分类图像的比例。
    • 召回率:特别针对每个类别(猫或狗),召回率显示了模型正确识别该类别图像的能力。
    • 精确度:精确度衡量的是在预测为特定类别的图像中,实际上属于该类别的比例。
    • F1分数:F1分数是精确度和召回率的调和平均,用于评估模型的整体性能。
  • 性能图表
    • 使用混淆矩阵来可视化模型在不同类别上的性能。
    • 绘制ROC曲线和AUC值,以评估模型在不同阈值下的分类能力。

2. 实验结果分析

  • 优点分析
    • 全局特征学习:Vision Transformer因其自注意力机制能够捕捉图像的全局特征,这可能导致在特定类型的图像上表现出色。
    • 泛化能力:如果模型在多种类型的猫狗图像(如不同品种、背景等)上表现良好,这表明它具有良好的泛化能力。
    • 高准确率:一个高准确率表明模型在大多数情况下能够正确分类图像。
  • 不足之处
    • 对特定特征的敏感性:如果模型在某些特定类型的图像(如特定背景或照明条件下的图像)上表现不佳,这可能暗示模型对于某些特征过于敏感。
    • 计算资源需求:ViT模型可能需要较多的计算资源,这在实际应用中可能是一个限制因素。
    • 训练时间:与某些传统CNN模型相比,ViT可能需要更长的训练时间,特别是在缺乏优化时。

五、结论:

实验的局限性和可改进之处

  • 数据集局限性:指出如果实验只用了特定类型的猫狗图像数据集,可能限制了模型泛化到更广泛场景的能力。
  • 计算资源需求:讨论实验中ViT模型相对较高的计算资源需求,以及这可能对实际应用造成的限制。
  • 训练时间和成本:指出模型训练所需的时间和成本,特别是在资源受限的环境中,这可能是一个重要考虑因素。
  • 改进建议
    • 数据多样性:增加数据集的多样性,包括不同的图像质量、光照条件、背景等,以进一步测试和提高模型的泛化能力。
    • 模型优化:探索模型架构和训练过程的优化,以减少资源消耗和提高训练效率。
    • 后续研究方向:建议未来研究可以探索将ViT与其他技术(如CNN)结合,以利用各自的优点,或开发更轻量级的ViT变体。

总体而言,这项实验展示了Vision Transformer在猫狗图像分类任务上的有效性和潜力,同时也揭示了其在数据和计算资源方面的一些局限性。未来的研究可以在这些发现的基础上进行,以实现更广泛的应用和更优的性能。

实验代码:

import copy
import torch
import torchvision
import torchvision.transforms as transforms
from torchvision import models
from torch.utils.data import DataLoader
from torch import optim, nn
from torch.optim import lr_scheduler
import os
import matplotlib.pyplot as plt
import warnings
import numpy as np

warnings.filterwarnings("ignore")
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 设置GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
import torch
from torchvision import datasets, transforms
import os

# 数据集路径
data_dir = "E:\深度学习\Vision Transformer\cats_and_dogs_small"

# 图像的大小
img_height = 224
img_width = 224

# 数据预处理
data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(img_height),
        transforms.RandomHorizontalFlip(),
        transforms.RandomVerticalFlip(),
        transforms.RandomRotation(0.2),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize((img_height, img_width)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

# 加载数据集
full_dataset = datasets.ImageFolder(data_dir)

# 获取数据集的大小
full_size = len(full_dataset)
train_size = int(0.7 * full_size)  # 假设训练集占80%
val_size = full_size - train_size  # 验证集的大小

# 随机分割数据集
torch.manual_seed(0)  # 设置随机种子以确保结果可重复
train_dataset, val_dataset = torch.utils.data.random_split(full_dataset, [train_size, val_size])

# 将数据增强应用到训练集
train_dataset.dataset.transform = data_transforms['train']

# 创建数据加载器
batch_size = 32
train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=0)
val_dataloader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=True, num_workers=0)

dataloaders = {'train': train_dataloader, 'val': val_dataloader}
dataset_sizes = {'train': len(train_dataset), 'val': len(val_dataset)}
class_names = full_dataset.classes
# 定义Vision Transformer模型
import timm

model = timm.create_model('vit_base_patch16_224',
                          pretrained=True)  # 你可以选择适合你需求的Vision Transformer版本,这里以vit_base_patch16_224为例
num_ftrs = model.head.in_features

# 根据分类任务修改最后一层
model.head = nn.Linear(num_ftrs, len(class_names))

model = model.to(device)

# 打印模型摘要
print(model)
# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器
optimizer = optim.Adam(model.parameters())

# 定义学习率调度器
exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)

# 开始训练模型
num_epochs = 10
best_model_wts = copy.deepcopy(model.state_dict())
best_acc = 0.0

# 初始化记录器
train_loss_history = []
train_acc_history = []
val_loss_history = []
val_acc_history = []

for epoch in range(num_epochs):
    print('Epoch {}/{}'.format(epoch, num_epochs - 1))
    print('-' * 10)

    # 每个epoch都有一个训练和验证阶段
    for phase in ['train', 'val']:
        if phase == 'train':
            model.train()  # Set model to training mode
        else:
            model.eval()  # Set model to evaluate mode

        running_loss = 0.0
        running_corrects = 0

        # 遍历数据
        for inputs, labels in dataloaders[phase]:
            inputs = inputs.to(device)
            labels = labels.to(device)

            # 零参数梯度
            optimizer.zero_grad()

            # 前向
            with torch.set_grad_enabled(phase == 'train'):
                outputs = model(inputs)
                _, preds = torch.max(outputs, 1)
                loss = criterion(outputs, labels)

                # 只在训练模式下进行反向和优化
                if phase == 'train':
                    loss.backward()
                    optimizer.step()

            # 统计
            running_loss += loss.item() * inputs.size(0)
            running_corrects += torch.sum(preds == labels.data)

        epoch_loss = running_loss / dataset_sizes[phase]
        epoch_acc = (running_corrects.double() / dataset_sizes[phase]).item()

        # 记录每个epoch的loss和accuracy
        if phase == 'train':
            train_loss_history.append(epoch_loss)
            train_acc_history.append(epoch_acc)
        else:
            val_loss_history.append(epoch_loss)
            val_acc_history.append(epoch_acc)

        print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))

        # 深拷贝模型
        if phase == 'val' and epoch_acc > best_acc:
            best_acc = epoch_acc
            best_model_wts = copy.deepcopy(model.state_dict())

    print()

print('Best val Acc: {:4f}'.format(best_acc))
epoch = range(1, len(train_loss_history) + 1)

fig, ax = plt.subplots(1, 2, figsize=(10, 4))
ax[0].plot(epoch, train_loss_history, label='Train loss')
ax[0].plot(epoch, val_loss_history, label='Validation loss')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('Loss')
ax[0].legend()

ax[1].plot(epoch, train_acc_history, label='Train acc')
ax[1].plot(epoch, val_acc_history, label='Validation acc')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Accuracy')
ax[1].legend()

plt.savefig("loss-acc.pdf", dpi=300,format="pdf")
from sklearn.metrics import classification_report, confusion_matrix
import math
import pandas as pd
import numpy as np
import seaborn as sns
from matplotlib.pyplot import imshow


# 定义一个绘制混淆矩阵图的函数
def plot_cm(labels, predictions):
    # 生成混淆矩阵
    conf_numpy = confusion_matrix(labels, predictions)
    # 将矩阵转化为 DataFrame
    conf_df = pd.DataFrame(conf_numpy, index=class_names, columns=class_names)

    plt.figure(figsize=(8, 7))

    sns.heatmap(conf_df, annot=True, fmt="d", cmap="BuPu")

    plt.title('Confusion matrix', fontsize=15)
    plt.ylabel('Actual value', fontsize=14)
    plt.xlabel('Predictive value', fontsize=14)


def evaluate_model(model, dataloader, device):
    model.eval()  # 设置模型为评估模式
    true_labels = []
    pred_labels = []
    # 遍历数据
    for inputs, labels in dataloader:
        inputs = inputs.to(device)
        labels = labels.to(device)

        # 前向
        with torch.no_grad():
            outputs = model(inputs)
            _, preds = torch.max(outputs, 1)

        true_labels.extend(labels.cpu().numpy())
        pred_labels.extend(preds.cpu().numpy())

    return true_labels, pred_labels


# 获取预测和真实标签
true_labels, pred_labels = evaluate_model(model, dataloaders['val'], device)

# 计算混淆矩阵
cm_val = confusion_matrix(true_labels, pred_labels)
a_val = cm_val[0, 0]
b_val = cm_val[0, 1]
c_val = cm_val[1, 0]
d_val = cm_val[1, 1]

# 计算各种性能指标
acc_val = (a_val + d_val) / (a_val + b_val + c_val + d_val)  # 准确率
error_rate_val = 1 - acc_val  # 错误率
sen_val = d_val / (d_val + c_val)  # 灵敏度
sep_val = a_val / (a_val + b_val)  # 特异度
precision_val = d_val / (b_val + d_val)  # 精确度
F1_val = (2 * precision_val * sen_val) / (precision_val + sen_val)  # F1值
MCC_val = (d_val * a_val - b_val * c_val) / (
    np.sqrt((d_val + b_val) * (d_val + c_val) * (a_val + b_val) * (a_val + c_val)))  # 马修斯相关系数

# 打印出性能指标
print("验证集的灵敏度为:", sen_val,
      "验证集的特异度为:", sep_val,
      "验证集的准确率为:", acc_val,
      "验证集的错误率为:", error_rate_val,
      "验证集的精确度为:", precision_val,
      "验证集的F1为:", F1_val,
      "验证集的MCC为:", MCC_val)

# 绘制混淆矩阵
plot_cm(true_labels, pred_labels)

# 获取预测和真实标签
train_true_labels, train_pred_labels = evaluate_model(model, dataloaders['train'], device)
# 计算混淆矩阵
cm_train = confusion_matrix(train_true_labels, train_pred_labels)
a_train = cm_train[0, 0]
b_train = cm_train[0, 1]
c_train = cm_train[1, 0]
d_train = cm_train[1, 1]
acc_train = (a_train + d_train) / (a_train + b_train + c_train + d_train)
error_rate_train = 1 - acc_train
sen_train = d_train / (d_train + c_train)
sep_train = a_train / (a_train + b_train)
precision_train = d_train / (b_train + d_train)
F1_train = (2 * precision_train * sen_train) / (precision_train + sen_train)
MCC_train = (d_train * a_train - b_train * c_train) / (
    math.sqrt((d_train + b_train) * (d_train + c_train) * (a_train + b_train) * (a_train + c_train)))
print("训练集的灵敏度为:", sen_train,
      "训练集的特异度为:", sep_train,
      "训练集的准确率为:", acc_train,
      "训练集的错误率为:", error_rate_train,
      "训练集的精确度为:", precision_train,
      "训练集的F1为:", F1_train,
      "训练集的MCC为:", MCC_train)

# 绘制混淆矩阵
plot_cm(train_true_labels, train_pred_labels)
from sklearn import metrics
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
import pandas as pd
import math


def plot_roc(name, labels, predictions, **kwargs):
    fp, tp, _ = metrics.roc_curve(labels, predictions)

    plt.plot(fp, tp, label=name, linewidth=2, **kwargs)
    plt.plot([0, 1], [0, 1], color='orange', linestyle='--')
    plt.xlabel('False positives rate')
    plt.ylabel('True positives rate')
    ax = plt.gca()
    ax.set_aspect('equal')


# 确保模型处于评估模式
model.eval()

train_ds = dataloaders['train']
val_ds = dataloaders['val']

val_pre_auc = []
val_label_auc = []

for images, labels in val_ds:
    for image, label in zip(images, labels):
        img_array = image.unsqueeze(0).to(device)  # 在第0维增加一个维度并将图像转移到适当的设备上
        prediction_auc = model(img_array)  # 使用模型进行预测
        val_pre_auc.append(prediction_auc.detach().cpu().numpy()[:, 1])
        val_label_auc.append(label.item())  # 使用Tensor.item()获取Tensor的值
auc_score_val = metrics.roc_auc_score(val_label_auc, val_pre_auc)

train_pre_auc = []
train_label_auc = []

for images, labels in train_ds:
    for image, label in zip(images, labels):
        img_array_train = image.unsqueeze(0).to(device)
        prediction_auc = model(img_array_train)
        train_pre_auc.append(prediction_auc.detach().cpu().numpy()[:, 1])  # 输出概率而不是标签!
        train_label_auc.append(label.item())
auc_score_train = metrics.roc_auc_score(train_label_auc, train_pre_auc)

plot_roc('validation AUC: {0:.4f}'.format(auc_score_val), val_label_auc, val_pre_auc, color="red", linestyle='--')
plot_roc('training AUC: {0:.4f}'.format(auc_score_train), train_label_auc, train_pre_auc, color="blue", linestyle='--')
plt.legend(loc='lower right')
# plt.savefig("roc.pdf", dpi=300,format="pdf")

print("训练集的AUC值为:", auc_score_train, "验证集的AUC值为:", auc_score_val)

### Vision Transformer用于动作识别的实现与教程 Vision Transformer (ViT) 已经被广泛应用图像分类任务,并逐渐扩展到视频处理领域,特别是动作识别。通过将视频帧序列作为输入,ViT 可以捕捉时空特征来完成复杂的动作识别任务。 #### 数据预处理 为了适应 ViT 的需求,通常需要对原始视频数据进行特定格式转换: 1. 将连续帧组合成固定长度的时间片段; 2. 对这些时间片段应用空间裁剪和缩放操作以保持一致性; 3. 转换成适合模型接收的形式(如张量)[^1]。 ```python import torch from torchvision import transforms transform = transforms.Compose([ transforms.Resize((224, 224)), # Resize frames to match input size of the model transforms.ToTensor(), # Convert PIL Image or numpy.ndarray to tensor ]) def preprocess_video(video_frames): """Preprocess video frames.""" processed_frames = [] for frame in video_frames: img_tensor = transform(frame) processed_frames.append(img_tensor) return torch.stack(processed_frames).permute(1, 0, 2, 3) # Change dimensions order from TxCxHxW -> CxTxHxW ``` #### 构建基于ViT的动作识别网络结构 构建一个简单的基于 ViT 的神经网络架构来进行动作类别预测。这里采用 PyTorch 实现了一个简化版的例子。 ```python class ActionRecognitionTransformer(nn.Module): def __init__(self, num_classes=50, seq_len=8, embed_dim=768, depth=12, heads=12, mlp_ratio=4., qkv_bias=False, drop_rate=0., attn_drop_rate=0., norm_layer=nn.LayerNorm): super().__init__() self.patch_embed = PatchEmbed(seq_len=seq_len, patch_size=(16, 16), in_chans=3, embed_dim=embed_dim) self.pos_embed = nn.Parameter(torch.zeros(1, seq_len + 1, embed_dim)) self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] self.blocks = nn.Sequential(*[ Block(dim=embed_dim, num_heads=heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i], norm_layer=norm_layer) for i in range(depth)]) self.norm = norm_layer(embed_dim) self.head = nn.Linear(embed_dim, num_classes) def forward(self, x): B = x.shape[0] cls_tokens = self.cls_token.expand(B, -1, -1) patches = self.patch_embed(x) pos_embed = self.interpolate_pos_encoding(patches.size(-2), patches.device) patches += pos_embed[:, :patches.size(1)] tokens = torch.cat([cls_tokens, patches], dim=1) out = self.blocks(tokens) out = self.norm(out) logits = self.head(out.mean(dim=1)) return logits model = ActionRecognitionTransformer() print(model(preprocessed_input)) ``` 此代码展示了如何定义一个基本的 Vision Transformer 来执行动作识别的任务。实际部署时可能还需要考虑更多细节优化以及针对具体应用场景调整参数设置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangzaojun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值