PyTorch深度学习实战 |基于ViT(Vision Transformer)网络花分类任务

简介: 本文介绍了基于PyTorch的ViT(Vision Transformer)模型在花卉分类任务中的实战应用。主要内容包括: 数据集准备:使用包含5类花卉(雏菊、蒲公英、玫瑰、向日葵、郁金香)的数据集,按8:2比例划分为训练集和验证集。 模型架构:实现了一个精简版ViT模型,包含Patch Embedding、CLS Token、位置嵌入和Transformer编码器等核心组件。 训练流程:详细展示了数据加载、模型训练、验证及测试的完整代码实现,包括损失函数、优化器和学习率调度等配置。 辅助功能:提供了设备选

 数据集介绍

花分类数据集:

百度云链接下载: https://pan.baidu.com/s/1QLCTA4sXnQAw_yvxPj9szg

提取码:58p0

下载好之后,解压到flower_data文件夹下,此时flower_data\flower_photos下就是放的我们的数据

集,我们看一下原始的数据是什么样子的:

分类类别:共包含 5 类花卉,对应 5 个文件夹: daisy(雏菊) dandelion(蒲公英) roses(玫

瑰) sunflowers(向日葵) tulips(郁金香)

image.gif

跑过一些项目的应该都有印象,比如YOLO等,他们的数据集的放置是有要求的一般情况下都是分

成两个,一个是train文件夹,train文件夹下是各种分类的文件夹(每个文件夹的名字是类报名)。

另外一个是val文件夹,val文件夹下是各种分类的文件夹(每个文件夹的名字是类报名)。一般是

按照8:2的比例去分这两个数据集的。这里的话可以用AI写代码整理,但是别忘记了检查一下。

image.gif

整理好之后:

训练集的路径:D:\vscode\shenduxvexishizhan\VIT\flower_data\train

验证集的路径是:D:\vscode\shenduxvexishizhan\VIT\flower_data\val


打怪升级路线

下面我们将会从下面几个部分来做这个项目:

                                         (1)网络结构模块

                                         (2)数据集读取模块

                                         (3)训练文件模块

                                         (4)测试文件模块

                                         (5)辅助函数模块

image.gif


网络结构模块

这个模块在前面的文章已经详细介绍了:PyTorch深度学习实战 |手算ViT(Vision Transformer)模型-CSDN博客

假设我们的最后是1000个分类的:

假设原始图像的大小是224*224*3的

输入端:

【1】经过卷积,然后拉平之后变成196×768大小的张量

【2】经过Class Embedding,变成了197×768大小的张量

【3】经过Position Embedding,变成了197×768大小的张量

编码器:

【1】经过编码器之后,大小不发生变化,仍然是197×768大小的张量

【2】提取CLS Token,大小是1×768

输出端:

【1】经过一个线性层,变成了1×1000的张量,每个数字代表着这个类别的概率

代码实现:

import torch
import torch.nn as nn
import torch.nn.functional as F
# 极简ViT实现(仅保留核心逻辑)
class ViT(nn.Module):
    def __init__(self, img_size=224, patch_size=16, num_classes=5, embed_dim=384, num_heads=6, num_layers=6):
        super().__init__()
        # 1. Patch Embedding
        self.num_patches = (img_size // patch_size) ** 2
        self.patch_embed = nn.Conv2d(3, embed_dim, kernel_size=patch_size, stride=patch_size)
        
        # 2. CLS Token + 位置嵌入
        self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
        self.pos_embed = nn.Parameter(torch.zeros(1, self.num_patches + 1, embed_dim))
        
        # 3. Transformer编码器(简化版)
        self.transformer = nn.Sequential(*[
            nn.TransformerEncoderLayer(
                d_model=embed_dim, 
                nhead=num_heads,
                dim_feedforward=embed_dim*4,
                activation='gelu',
                batch_first=True
            ) for _ in range(num_layers)
        ])
        
        # 4. 分类头
        self.norm = nn.LayerNorm(embed_dim)
        self.head = nn.Linear(embed_dim, num_classes)
    def forward(self, x):
        # Patch嵌入: [B,3,224,224] → [B,384,14,14] → [B,196,384]
        x = self.patch_embed(x).flatten(2).transpose(1, 2)
        
        # 添加CLS Token: [B,197,384]
        cls_token = self.cls_token.expand(x.shape[0], -1, -1)
        x = torch.cat((cls_token, x), dim=1)
        
        # 添加位置嵌入
        x = x + self.pos_embed
        
        # Transformer编码
        x = self.transformer(x)
        
        # 分类(仅用CLS Token)
        x = self.norm(x)[:, 0]
        x = self.head(x)
        return x
# 快速创建模型
def create_vit_model():
    return ViT()

image.gif

这段代码应该没有什么好说的了,模块最后的输出应该是batch*numclass的张量,其中batch,每

个批次的图片的数量,numclass表示预测的类别。批量花卉图片张量 [B,3,224,224]    分类预测得分 [B,5]


数据集读取模块

代码实现

import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# 数据集路径配置(替换为你的实际路径)
TRAIN_PATH = r"D:\vscode\shenduxvexishizhan\deep-learning-for-image-processing-master\flower_data\train"
VAL_PATH = r"D:\vscode\shenduxvexishizhan\deep-learning-for-image-processing-master\flower_data\val"
def get_data_loaders(batch_size=16):
    """获取训练/验证数据加载器"""
    # 训练集预处理(含简单增强)
    train_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
    
    # 验证/测试集预处理
    val_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
    
    # 加载数据集
    train_dataset = datasets.ImageFolder(TRAIN_PATH, transform=train_transform)
    val_dataset = datasets.ImageFolder(VAL_PATH, transform=val_transform)
    
    # 创建数据加载器
    train_loader = DataLoader(
        train_dataset, 
        batch_size=batch_size, 
        shuffle=True, 
        num_workers=0  # Windows下设为0
    )
    val_loader = DataLoader(
        val_dataset, 
        batch_size=batch_size, 
        shuffle=False, 
        num_workers=0
    )
    
    # 类别映射(用于推理)
    class2idx = train_dataset.class_to_idx
    idx2class = {v: k for k, v in class2idx.items()}
    
    return train_loader, val_loader, idx2class

image.gif


训练文件模块

代码实现

import torch
import torch.nn as nn
import torch.optim as optim
from tqdm import tqdm
from model import create_vit_model
from dataset import get_data_loaders
from utils import get_device, plot_curve, save_model
# 训练配置
BATCH_SIZE = 16
EPOCHS = 20
LR = 1e-4
WEIGHT_DECAY = 1e-4
def train_one_epoch(model, loader, loss_fn, optimizer, device):
    """单轮训练"""
    model.train()
    total_loss, total_acc = 0, 0
    pbar = tqdm(loader, desc='Training')
    
    for imgs, labels in pbar:
        imgs, labels = imgs.to(device), labels.to(device)
        
        # 前向传播
        outputs = model(imgs)
        loss = loss_fn(outputs, labels)
        
        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        # 统计指标
        acc = (outputs.argmax(1) == labels).float().mean()
        total_loss += loss.item()
        total_acc += acc.item()
        
        # 更新进度条
        pbar.set_postfix({'loss': loss.item(), 'acc': acc.item()})
    
    return total_loss/len(loader), total_acc/len(loader)
@torch.no_grad()
def validate(model, loader, loss_fn, device):
    """单轮验证"""
    model.eval()
    total_loss, total_acc = 0, 0
    pbar = tqdm(loader, desc='Validating')
    
    for imgs, labels in pbar:
        imgs, labels = imgs.to(device), labels.to(device)
        
        outputs = model(imgs)
        loss = loss_fn(outputs, labels)
        acc = (outputs.argmax(1) == labels).float().mean()
        
        total_loss += loss.item()
        total_acc += acc.item()
        pbar.set_postfix({'loss': loss.item(), 'acc': acc.item()})
    
    return total_loss/len(loader), total_acc/len(loader)
def main():
    # 1. 初始化
    device = get_device()
    print(f'Using device: {device}')
    
    # 2. 加载数据
    train_loader, val_loader, _ = get_data_loaders(BATCH_SIZE)
    
    # 3. 创建模型
    model = create_vit_model().to(device)
    loss_fn = nn.CrossEntropyLoss()
    optimizer = optim.AdamW(model.parameters(), lr=LR, weight_decay=WEIGHT_DECAY)
    scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)
    
    # 4. 训练记录
    train_losses, train_accs = [], []
    val_losses, val_accs = [], []
    best_acc = 0
    
    # 5. 训练循环
    for epoch in range(EPOCHS):
        print(f'\nEpoch {epoch+1}/{EPOCHS}')
        
        # 训练
        train_loss, train_acc = train_one_epoch(model, train_loader, loss_fn, optimizer, device)
        # 验证
        val_loss, val_acc = validate(model, val_loader, loss_fn, device)
        # 学习率调度
        scheduler.step()
        
        # 记录
        train_losses.append(train_loss)
        train_accs.append(train_acc)
        val_losses.append(val_loss)
        val_accs.append(val_acc)
        
        # 保存最优模型
        if val_acc > best_acc:
            best_acc = val_acc
            save_model(model)
            print(f'Best model saved! Val Acc: {best_acc:.4f}')
        
        # 打印本轮结果
        print(f'Train: Loss={train_loss:.4f}, Acc={train_acc:.4f}')
        print(f'Val:   Loss={val_loss:.4f}, Acc={val_acc:.4f}')
    
    # 6. 绘制训练曲线
    plot_curve(train_losses, train_accs, val_losses, val_accs)
    print(f'\nTraining finished! Best Val Acc: {best_acc:.4f}')
if __name__ == '__main__':
    main()

image.gif


测试代码模块

代码实现

import torch
from PIL import Image
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from model import create_vit_model
from dataset import VAL_PATH
from utils import get_device, load_model
# 测试配置
MODEL_PATH = 'best_model.pth'
TEST_IMG_PATH = r'D:\vscode\shenduxvexishizhan\deep-learning-for-image-processing-master\flower_data\val\roses\123.jpg'  # 替换为你的测试图片
def predict_single_image():
    """单张图片测试/推理"""
    # 1. 初始化
    device = get_device()
    model = create_vit_model().to(device)
    model = load_model(model, MODEL_PATH, device)
    model.eval()
    
    # 2. 获取类别映射
    from torchvision.datasets import ImageFolder
    val_dataset = ImageFolder(VAL_PATH)
    idx2class = {v: k for k, v in val_dataset.class_to_idx.items()}
    
    # 3. 图片预处理
    transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
    
    # 4. 加载并处理图片
    img = Image.open(TEST_IMG_PATH).convert('RGB')
    img_tensor = transform(img).unsqueeze(0).to(device)
    
    # 5. 推理
    with torch.no_grad():
        output = model(img_tensor)
        prob = torch.softmax(output, dim=1)
        pred_idx = output.argmax(1).item()
        pred_class = idx2class[pred_idx]
        pred_conf = prob[0][pred_idx].item()
    
    # 6. 可视化结果
    plt.imshow(img)
    plt.title(f'Prediction: {pred_class}\nConfidence: {pred_conf:.4f}')
    plt.axis('off')
    plt.show()
    
    # 7. 打印结果
    print('='*30)
    print(f'Test Image: {TEST_IMG_PATH}')
    print(f'Predicted Class: {pred_class}')
    print(f'Confidence: {pred_conf:.4f}')
    print('='*30)
    
    # 打印所有类别概率
    print('\nClass Probabilities:')
    for idx, cls in idx2class.items():
        print(f'{cls}: {prob[0][idx]:.4f}')
if __name__ == '__main__':
    predict_single_image()

image.gif


辅助函数模块

代码实现

import torch
import matplotlib.pyplot as plt
# 设备选择
def get_device():
    return torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 绘制训练曲线
def plot_curve(train_losses, train_accs, val_losses, val_accs):
    plt.figure(figsize=(12, 5))
    
    # 损失曲线
    plt.subplot(1,2,1)
    plt.plot(train_losses, label='Train Loss')
    plt.plot(val_losses, label='Val Loss')
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.legend()
    plt.title('Loss Curve')
    
    # 准确率曲线
    plt.subplot(1,2,2)
    plt.plot(train_accs, label='Train Acc')
    plt.plot(val_accs, label='Val Acc')
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.legend()
    plt.title('Accuracy Curve')
    
    plt.tight_layout()
    plt.savefig('training_curve.png')
    plt.show()
# 保存模型
def save_model(model, path='best_model.pth'):
    torch.save(model.state_dict(), path)
# 加载模型
def load_model(model, path='best_model.pth', device='cuda'):
    model.load_state_dict(torch.load(path, map_location=device))
    return model

image.gif


实验结果

显然是过拟合了,后面的教程中,我们再去学习如何解决这个问题

image.gif


目录
相关文章
|
5月前
|
机器学习/深度学习 PyTorch 测试技术
PyTorch深度学习实战 |多头注意力机制
摘要:本文详细介绍了Transformer中的多头自注意力机制,从整体结构到实现细节,包括四部分内容:(1)多头自注意力的基本架构;(2)内部计算流程解析;(3)注意力计算公式详解;(4)代码实现。重点阐述了多头并行的计算方式、缩放点积注意力的计算步骤(QK转置、缩放、softmax和加权求和),以及残差连接和层归一化的作用。通过"Are you OK?"示例展示了输入张量如何经过8个64维注意力头处理后拼接成512维输出。文章最后提供了完整的PyTorch实现代码,并附测试用例验证模型
357 0
|
5月前
|
机器学习/深度学习 编解码 算法
PyTorch深度学习实战 |手算​​U-net
本文详细解析了U-Net网络架构及其在医学图像分割中的应用。重点对比了U-Net与FCN的核心区别:U-Net采用特征拼接(Concat)保留所有层级信息,而FCN使用特征相加(Add)进行融合。文章深入剖析了U-Net的编码器-瓶颈-解码器结构,解释了其独特的裁剪拼接机制和Overlap-tile策略,并提供了完整的PyTorch实现代码。现代U-Net通过SamePadding实现了输入输出尺寸一致,显著提升了分割精度。文章还探讨了弹性形变数据增强和带空间权重的损失函数设计,为医学图像分析提供了实用解决
434 2
|
30天前
|
人工智能 数据可视化 API
阿里云百炼API模型平台:免费Tokens领取、控制台入口、模型定价、Token Plan及接入指南FAQ
阿里云百炼是面向企业与开发者的AI模型服务平台,集成通义千问及DeepSeek、Kimi等主流第三方模型,提供模型调用、应用构建、知识库、模型定制等全链路能力;新用户免费领超7000万Tokens。
554 0
|
2月前
|
人工智能 运维 监控
大模型多Agent核心能力:深入了解协作架构与任务调度,完整构建复杂AI协同任务22.7
本文系统解析大模型多Agent协作技术:阐明其解决单模型能力瓶颈的核心价值,深入剖析角色专属化、行为自主化、交互协同化三大特征;详解中心化、分层、分布式三类架构及适配场景;梳理任务拆解、智能分配、时序管控、异常兜底四大调度逻辑,并提供可落地的实践框架与代码示例。
377 1
|
2月前
|
人工智能 IDE 开发工具
全新 Qoder 线上发布会,今晚 19:00 不见不散!
9月1日19:00,Qoder线上发布会直播!聚焦全新 Qoder,产品、研发、设计三位成员深度解读,助你厘清 Qoder IDE与新 Qoder 的适用场景。锁定视频号「Qoder.ai」
268 1
|
3月前
|
SQL 人工智能 自然语言处理
95% 的业务分析交给 AI 后,Anthropic 数据团队做对了什么?
做过数据的人都知道,「让业务自己查数」听起来很美,落地却很痛。 第一条:把表做得又宽又全。 一张大宽表、一堆反规范化视图,非技术同事至少能点一点。但业务一长大,同名不同义、同义不同表就越来越多。「收入」「活跃用户」到底指什么,十个人可能有八个答案。
|
4月前
|
缓存 API
阿里云千问 Qwen3.7-Max 完整手册:模型能力、限时 5 折价格、免费 Tokens、OpenAI 兼容 API 代码
Qwen3.7-Max是阿里云Qwen3.7系列最强旗舰模型,专注智能体时代,擅编程、办公与长周期自主任务。支持文本输入/输出、Function Calling、缓存等能力,上下文达1M(输入991K),享免费100万Tokens及限时5折优惠,百炼平台即开即用。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
|
5月前
|
机器学习/深度学习 自然语言处理 PyTorch
PyTorch深度学习实战 |手动计算 Transformer和完整的代码实现
本文介绍了基于PyTorch实现Transformer模型的完整过程。主要内容包括:1)Transformer架构的核心组件实现,如多头注意力机制、位置前馈网络、位置编码等;2)模型构建步骤,包括词嵌入层、编码器/解码器块和输出层的实现;3)完整的训练流程,包含数据处理、损失计算和参数优化;4)评估方法验证模型性能。文章通过代码示例详细展示了如何从零开始构建Transformer,并应用于机器翻译任务,同时对模型各层的输入输出维度进行了说明。该实现可作为深度学习实践者学习Transformer架构的实用指南
414 0
|
3月前
|
人工智能 架构师 安全
【AI时代软件项目管理系列】2. 从工具到数字员工:AI 在项目团队中的角色演进
AI 正从个人助手演进为能够规划、调用工具并完成任务的 Agent,进一步形成多 Agent 协作与软件工厂。角色变化带来的不只是效率提升,更是团队结构、任务分配、进度衡量和质量管理方式的重构。项目经理需要把模型、Agent、知识库和自动化流程纳入统一管理,通过明确任务契约、权限边界、质量门禁与人工验收,确保 AI 执行可控、结果可验证、责任可追溯。
381 0
|
4月前
|
存储 API 数据处理
阿里云智能媒体管理(IMM)对接使用全攻略:从开通到生产级实践
本文全面解析阿里云智能媒体管理(IMM)的对接与使用。首先介绍IMM的产品定位与服务架构,阐明其与OSS的深度集成关系。然后详细说明开通服务、创建项目、绑定Bucket的全流程操作,并给出Java和Python两种主流语言的SDK初始化与API调用示例。接着深入讲解文档格式转换、文档预览、视频截帧、图片智能检测等核心功能的实现方法,涵盖同步与异步处理两种模式。同时针对权限配置、新旧版本差异、计费规则、性能优化等关键问题进行专项剖析,帮助读者构建生产级的媒体处理能力。全文基于新版IMM(API版本2020-09-30)撰写,适合开发者、架构师及技术决策者阅读。