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

在线体验各类最新模型,更有模型 免费Token 额度领取!
立即体验
简介: 本文介绍了基于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


目录
相关文章
|
28天前
|
机器学习/深度学习 数据采集 人工智能
田间杂草检测数据集分享(适用于YOLO系列深度学习分类检测任务)
本数据集含4000张真实农田图像(小麦/玉米/水稻田),YOLO格式标注杂草目标,覆盖多天气、光照与视角,适用于YOLO系列等目标检测模型训练,助力智能除草与精准农业研究。(239字)
361 16
|
28天前
|
人工智能 机器人 芯片
人工智能|YOLOv8实战
本内容为安全帽检测实战项目,基于YOLOv8模型,涵盖Kaggle数据获取、自定义yaml配置、模型训练(yolo_train.py)与测试(yolo_test.py),并提供服务器(FastAPI+Docker)、边缘(Jetson+TensorRT)及国产嵌入式(RK3588+RKNN)三类部署方案,支持工业场景实时智能识别。(239字)
312 1
|
28天前
|
存储 人工智能 算法
告别无效刷屏!TrendRadar:最快30秒部署的开源热点助手,让你只看真正关心的新闻
TrendRadar 是一个轻量级、易部署的热点新闻聚合与推送工具。它能够从知乎、抖音、B站、微博、百度、华尔街见闻等11个主流平台抓取热搜榜单,然后根据你设定的关键词进行智能筛选,最终将你最关心的内容推送到手机或邮箱。
419 13
 告别无效刷屏!TrendRadar:最快30秒部署的开源热点助手,让你只看真正关心的新闻
|
28天前
|
人工智能 供应链 数据可视化
长江商学院CIO徐斌:AI时代,组织的进化逻辑与人才转型新思维
徐斌,长江商学院CIO、计算机博士,20年世界500强及上市公司高管经验,首创数字化“三驾马车”方法论(流程变革、IT固化、数字运营),成功主导得力集团全链路转型,助力其获评首批浙江省未来工厂。
|
28天前
|
API
阿里云微服务引擎 MSE 及 API 网关 2026 年 5 月产品动态
阿里云微服务引擎 MSE 及 API 网关 2026 年 5 月产品动态。
205 24
|
18天前
|
人工智能 缓存 运维
CC Switch路由代理技术解析:Codex CLI无缝对接DeepSeek模型实操指南
在现代AI开发与命令行智能编程场景中,Codex CLI是开发者常用的命令行智能辅助工具,能够实现代码生成、问题排查、脚本编写、项目调试等自动化能力。但原生Codex CLI存在明显的适配局限,其底层仅兼容OpenAI Responses API协议,无法直接对接DeepSeek等主流第三方大模型。市面上绝大多数第三方开源、商用大模型均采用Chat Completions API协议,两种协议在请求结构、参数格式、流式返回规则、响应字段定义上完全不互通,直接填写第三方模型接口地址会出现接口404报错、参数解析失败、流式内容中断、模型列表加载异常等各类问题,极大限制了Codex CLI的模型拓展
580 1
|
28天前
|
人工智能 Kubernetes 安全
【重磅】 Blade AI 自主韧性测试智能体正式开源
本次阿里云峰会上发布韧性测试智能体 Blade AI:用自然语言一句话自动完成系统韧性测试全流程。
400 14
|
28天前
|
人工智能 缓存 弹性计算
阿里云服务器2核4G5M199元解析:独享型u1实例,性能、适用场景、购买和续费规则介绍
阿里云通用算力型u1实例(ecs.u1-c1m2.large)2核4G、5M带宽、80G ESSD Entry云盘,活动特惠价仅199元/年(官网价3498.36元),企业新老用户同享,续费同价至2027年3月31日,每人限购1台。该实例采用独享型架构,搭载Intel至强可扩展处理器,内网带宽1Gbit/s、收发包30万PPS、云盘IOPS 1万,性能稳定,适合企业官网、中小Web应用、轻量数据库及开发测试等场景。
|
28天前
|
安全 JavaScript 前端开发
《ZAKU渗透论:卓伊凡的2026渗透工程》第四章:Web攻击原理(下)——XSS、CSRF、文件上传漏洞
本章详解XSS、CSRF与文件上传三大Web漏洞:XSS通过注入恶意脚本窃取Cookie;CSRF伪造已登录用户请求执行非自愿操作;文件上传漏洞则因校验缺失致服务器被控。三者共性——过度信任用户输入。(239字)
341 10
|
28天前
|
SQL 安全 测试技术
《ZAKU渗透论:卓伊凡的2026渗透工程》第一章:黑客是怎么工作的?
渗透测试是授权下模拟黑客攻击,检验系统安全性;白帽合法防护,黑帽非法入侵,灰帽亦违法。攻击分7步:侦察、武器化、投递、利用、安装、C2、目标达成。它不同于自动化漏洞扫描,重在人工验证与深度分析。(239字)
266 6

热门文章

最新文章