当前位置: 首页 > news >正文

PyTorch图像建模(图像识别、分割和分类案例)

文章目录

  • 图像分类技术:改变生活的智能之眼
  • 图形识别技术
    • 图像识别过程
    • 图像预处理
    • 图像特征提取
  • 图像分割技术
  • 练习案例:图像分类
    • 项目源码地址
    • 实现代码(简化版)
    • 训练结果(简化版)
    • 实现代码(优化版)
    • 训练结果(优化版)
    • 存在的问题

图像分类技术:改变生活的智能之眼

  • 图像分类技术已经深入我们的日常生活,应用场景越来越广泛。比如,手机相册能自动识别人物照片,社交平台可以精准识别照片中的好友和物体,自动驾驶汽车能实时检测道路状况……这些应用都表明,图像分类技术正成为我们生活中不可或缺的一部分。

  • 传统方法的局限:早期的图像分类技术主要依赖数字图像处理和机器学习方法。研究人员需要手动提取图像特征(如颜色、纹理等),再根据这些特征对图像进行分类。但这种方法有个明显缺点:适应性差。如果图像受到噪声干扰或部分残缺,分类效果就会大打折扣。

  • 进入大数据时代后,数据量爆炸式增长,传统算法的计算压力越来越大。这时,人工神经网络(ANN)成为了一种解决方案。然而,传统ANN也存在问题: 计算成本高:采用全连接结构,参数数量庞大,扩展性差; 忽略空间信息:未考虑像素之间的位置关系,而这对图像分析至关重要;层数限制:网络层数越多,表达能力越强,但误差反向传播难以超过3层,限制了性能提升。

  • 卷积神经网络(CNN)的突破:CNN的提出完美解决了上述问题,迅速成为计算机视觉的核心技术。它的设计灵感来自图像的特性:相邻像素之间具有强相关性。CNN通过局部连接和权值共享,大幅减少了无效计算,同时充分利用了像素的空间关系和梯度信息。


  • 简单来说,CNN就像一位“智能画家”,不仅能捕捉图像的细节,还能理解这些细节之间的联系,从而让图像分类更高效、更准确。正是这种优势,让它成为当今人工智能领域的重要基石。

图形识别技术

  • 图像识别指利用信息处理与计算机技术,采用数学方法,对图像进行处理、分析和理解的过程。图像识别技术的使用领域广泛,如指纹识别、虹膜识别、手写汉字识别、交通标志识别、手势识别、人脸识别、机器人视觉等,分类识别的事物种类丰富,被识别对象的内容复杂。例如,在交通管理系统中,通过使用车牌的自动识别来记录车辆的违章行为;在医学图像中,根据细胞的形状和颜色等分析是否发生病变;通过植物的颜色和形态长势判断何时需要浇水、施肥;通过气象观测的数据或利用卫星照片来进行天气预报等。

图像识别过程

  • 图像识别过程大致分为两个阶段:样本训练阶段和图像识别阶段。
  1. 样本训练阶段:对大量样本进行预处理、提取图像特征、进行模式分类,获取一个样本图像特征库。
  2. 图像识别阶段:对输入图形进行预处理、图像分析、图形分割、提取关注部分的图像特征、利用模式识别方法对特征和图像特征库中的特征进行相关处理。当图像匹配失败时,将其特征作为新的模式分类并入图像特征库。

图像预处理

  • 图像预处理服务于后续的图像识别服务。预处理过程包括灰度处理、归一化处理、低通滤波处理、均值滤波处理和中值滤波处理、高通滤波处理、边缘检测、边界检测、区域连接和门限等技术。
  • 彩色图像的灰度处理:方便分析图形内容。
  • 直方图归一化、低通滤波、均值滤波和中值滤波处理:可以减少图像在成像过程中收到的噪声污染。
  • 高通滤波处理:突出图像的细节特征。
  • 边缘检测、边界检测、区域连接和门限等技术:方便能够快速找到关注的图像部分。

图像特征提取

  • 图像特征提取能保证图像的大小、位移及旋转的不变性,以提取到唯一标识图像特性的特征来为图像识别服务。
  • 图像特征提取实际上是图像表示问题,它的目的是减轻图像在识别过程中的负担。因为原始图像的数据维数非常高,通过特征提取给数据降维,从而提高识别效率和识别率,为节省资源、构造和设计特征分类器带来益处。

图像分割技术

  • 图像分类就是将图像分成若干个特定的、具有独特性质的区域并提取感兴趣的目标的技术和过程。主要的图形分割技术主要分为以下几类:基于阈值的分割方法、基于区域的分割方法、基于边缘的分割方法以及基于特定理论的分割方法等。
  1. 基于阈值的图像分割技术:最常用的并行分割技术,基于阈值的分割方法进行输入图像到输出图像的变换。关键点是确定阈值,确定阈值后将阈值与像素点的灰度值并行地逐个进行比较,分割的结果直接给出图像区域。
  2. 基于区域的分割技术:典型的串行区域分割技术,主要包括区域生长和分割合并两种方法。区域生长是从某个或者某些像素点出发,最后得到整个区域,进行实现目标提取;分割合并差不多是区域生长的逆过程,从整个图像出发,不断分割得到各个子区域,然后把前景区域合并,实现目标提取。
  3. 基于边缘的分割技术:检测灰度级或者结果有突变的地方,表明一个区域的终结(另一个区域的开始)。不同图像的灰度不同,边界处一般有明显的边缘,利用此特征可以分割图像。
  4. 基于特定理论的分割技术:如模糊集理论具有描述试图不确定性的能力,适合用于图像分割问题。模糊分割技术在图像分割方面的应用,一个显著特点就是它能和现有的许多图像分割方法结合,形成一系列的集成模糊分割技术,如模糊聚、模糊阈值、模糊边缘检测技术。

练习案例:图像分类

  • 数据集使用CIFAR10,数据集一共包含10个类别的RGB彩色图片,10个分类分别为飞机、汽车、鸟、猫、鹿、狗、蛙、马、船、卡车。图片尺寸为32×32,数据集中一共有50000正训练图片和10000张测试图片。

项目源码地址

  • image_classification_and_recognition

实现代码(简化版)

import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn.functional as f
import torchvision
from torchvision.transforms import transforms# 0 torchvision的数据集是基于PILImage,数值范围[0,1]需要转化范围为[-1,1]的张量
transform = transforms.Compose([transforms.ToTensor(),  # 将数据转化为张量# 归一化处理 均值0.5 标准差0.5transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
# 1 导入训练集和测试数据集 并设置数据加载参数 定义数据集中的类别名称
"""
torchvision.datasets.CIFAR10 定义CIFAR-10数据集
root='./data' 指定数据集的根目录
train=True 创建训练集
download=True 自动下载数据集 如果数据集不存在报错
transform=transform 应用数据预处理变换
"""
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
"""
torch.utils.data.DataLoader 定义数据加载器 加载训练数据
train_dataset 加载的数据集对象 训练数据集
batch_size=4  每个批次包含的样本数量
shuffle=False 不在每个 epoch 开始时打乱数据顺序
num_workers=4 使用 4 个子进程加载数据(加快数据读取速度)
"""
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=4, shuffle=False, num_workers=4)"""
torchvision.datasets.CIFAR10 定义CIFAR-10数据集
root='./data' 指定数据集的根目录
train=False 创建测试集
download=True 自动下载数据集 如果数据集不存在报错
transform=transform 应用数据预处理变换
"""
test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
"""
torch.utils.data.DataLoader 定义数据加载器 加载测试数据
train_dataset 加载的数据集对象 训练数据集
batch_size=4  每个批次包含的样本数量
shuffle=True 不在每个 epoch 开始时打乱数据顺序
num_workers=4 使用 4 个子进程加载数据(加快数据读取速度)
"""
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=4, shuffle=True, num_workers=4)
# 定义类别名
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')# 2 搭建网络模型
class Net(torch.nn.Module):def __init__(self):super(Net, self).__init__()# 定义卷积层self.conv1 = torch.nn.Conv2d(3, 6, 5)self.pool = torch.nn.MaxPool2d(2, 2)self.conv2 = torch.nn.Conv2d(6, 16, 5)# 定义全连接层self.fc1 = torch.nn.Linear(16 * 5 * 5, 120)self.fc2 = torch.nn.Linear(120, 84)self.fc3 = torch.nn.Linear(84, 10)def forward(self, x):x = self.pool(f.relu(self.conv1(x)))x = self.pool(f.relu(self.conv2(x)))x = x.view(-1, 16 * 5 * 5)x = f.relu(self.fc1(x))x = f.relu(self.fc2(x))x = self.fc3(x)return x"""
An attempt has been made to start a new process before thecurrent process has finished its bootstrapping phase.This probably means that you are not using fork to start yourchild processes and you have forgotten to use the proper idiomin the main module:if __name__ == '__main__':freeze_support()...The "freeze_support()" line can be omitted if the programis not going to be frozen to produce an executable.
""""""
image_show 显示图片
img: 接受一个图像张量img 
进行归一化处理,将其转化为Numpy数组,然后使用plt.imshow显示图像
"""def image_show(img):"""显示图片:param img:  要显示的图像:return: 无"""# 对图像归一化操作img = img / 2 + 0.5# 转化为张量img_np = img.numpy()# 以正常通道顺序显示图片plt.imshow(np.transpose(img_np, (1, 2, 0)))if __name__ == '__main__':# 创建Net实例net = Net()#  定义设备device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")net.to(device)  # 👈 添加这一行!# 3 训练网络模型# 定义交叉熵损失函数和随机梯度下降优化器(学习率为0.01 动量为0.9)criterion = torch.nn.CrossEntropyLoss()optimizer = torch.optim.SGD(net.parameters(), lr=0.001, momentum=0.9)# 循环训练nums_epoch = 2for epoch in range(nums_epoch):running_loss = 0.0# 一次循环 50000/4=12500次for i, (inputs, labels) in enumerate(train_loader, 0):inputs, labels = inputs.to(device), labels.to(device)optimizer.zero_grad()# RuntimeError: Input type (torch.cuda.FloatTensor) and weight type (torch.FloatTensor) should be the same 添加 net.to(device)  # 👈 添加 保证两个数据不在同一个设备上outputs = net(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()running_loss += loss.item()if i % 3000 == 2999:print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 3000))running_loss = 0.0print("训练结束")# 应用网络模型# 迭代测试集加速器 抽取测试集中的4张图片 每个批次包含 4 张图像(因为 batch_size=4)data_iter = iter(test_loader)# 使用 next(data_iter) 获取第一个批次的数据,其中包含 4 张图像和对应的标签。images, labels = next(data_iter)# torchvision.utils.make_grid(images) 将这 4 张图像拼接成一个网格形式,便于显示image_show(torchvision.utils.make_grid(images))# 打印图片真实分类print('图像真实分类:', ' '.join('%5s' % classes[labels[j]] for j in range(4)))# 将图像images输入到模型net,得到模型输出outputs = net(images.to(device))# 获取模型预测结果 在输出的第一维(每个图像)上找到最大的值和索引 1表示在第一维上进行最大值搜索_, predicted = torch.max(outputs, 1)print('图像预测分类:', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))# 定义预测数量和总预测值初始化为0correct, total = 0, 0# 禁用梯度计算 进行测试with torch.no_grad():# 遍历测试集的数据和对应的标签for images, labels in test_loader:# 将图像输入到模型net,得到模型输出outputs = net(images.to(device))# 在输出的第一维(每个图像)上找到最大的值和索引 得到预测的类别predicted_, predicted = torch.max(outputs.data, 1)# 累计总预测数量total 通过labels.size(0) 获取标签数量total += labels.size(0)# 累计预测正确的数量correct 通过判断预测的类别predicted 是否等于标签labels .sum().item将张量转化为整数correct += (predicted == labels.to(device)).sum().item()# 打印测试集的准确率print('预测准确率: %d %%' % (100 * correct / total))

训练结果(简化版)

[1,  3000] loss: 2.146
[1,  6000] loss: 1.778
[1,  9000] loss: 1.606
[1, 12000] loss: 1.511
[2,  3000] loss: 1.423
[2,  6000] loss: 1.374
[2,  9000] loss: 1.327
[2, 12000] loss: 1.288
训练结束
图像真实分类:   cat deer deerplane
图像预测分类:  bird deerhorse ship
预测准确率: 55 %

实现代码(优化版)

  1. 优化点1:使用ResNet18预训练模型
  2. 优化点2:增大batch_size = 256
  3. 优化点3:预先加载数据prefetch_factor=2
  4. 优化点4:使用更合适的优化器(如 Adam)并调整学习率。
  5. 优化点5:使用混合精度scaler = torch.amp.GradScaler('cuda')
  6. 优化点6:
import os
import matplotlib.pyplot as plt
import numpy as np
import torch
import torchvision
from torch.amp import autocast
from torchvision.transforms import transforms
import torchvision.models as models# 0 torchvision的数据集是基于PILImage,数值范围[0,1]需要转化范围为[-1,1]的张量
transform = transforms.Compose([# 优化4 在数据预处理阶段添加数据增强操作,提高模型泛化能力。transforms.RandomHorizontalFlip(),  # 随机水平翻转transforms.RandomRotation(10),  # 随机旋转transforms.ToTensor(),  # 将数据转化为张量# 归一化处理 均值0.5 标准差0.5transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
batch_size = 256
# 1 导入训练集和测试数据集 并设置数据加载参数 定义数据集中的类别名称
"""
torchvision.datasets.CIFAR10 定义CIFAR-10数据集
root='./data' 指定数据集的根目录
train=True 创建训练集
download=True 自动下载数据集 如果数据集不存在报错
transform=transform 应用数据预处理变换
"""
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
"""
torch.utils.data.DataLoader 定义数据加载器 加载训练数据
train_dataset 加载的数据集对象 训练数据集
batch_size=4  每个批次包含的样本数量
shuffle=True 在每个 epoch 开始时打乱数据顺序
num_workers=4 使用 4 个子进程加载数据(加快数据读取速度)
"""
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4,pin_memory=True,prefetch_factor=2)"""
torchvision.datasets.CIFAR10 定义CIFAR-10数据集
root='./data' 指定数据集的根目录
train=False 创建测试集
download=True 自动下载数据集 如果数据集不存在报错
transform=transform 应用数据预处理变换
prefetch_factor=2 提前加载2个batch_size
"""
test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
"""
torch.utils.data.DataLoader 定义数据加载器 加载测试数据
train_dataset 加载的数据集对象 训练数据集
batch_size=4  每个批次包含的样本数量
shuffle=False 不在每个 epoch 开始时打乱数据顺序
num_workers=4 使用 4 个子进程加载数据(加快数据读取速度)
prefetch_factor=2 提前加载2个batch_size
"""
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=4,pin_memory=True,prefetch_factor=2)
# 定义类别名
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')"""
image_show 显示图片
img: 接受一个图像张量img 
进行归一化处理,将其转化为Numpy数组,然后使用plt.imshow显示图像
"""
def image_show(img):"""显示图片:param img:  要显示的图像:return: 无"""# 对图像归一化操作img = img / 2 + 0.5# 转化为张量img_np = img.numpy()# 以正常通道顺序显示图片plt.imshow(np.transpose(img_np, (1, 2, 0)))if __name__ == '__main__':# 设置模型保存路径model_dir = "./data"os.makedirs(model_dir, exist_ok=True)# 指定模型文件路径model_path = os.path.join(model_dir, "resnet18-f37072fd.pth")# 如果模型不存在,则自动下载到指定路径if not os.path.exists(model_path):# 下载模型权重到指定路径resnet18_url = "https://download.pytorch.org/models/resnet18-f37072fd.pth"torch.hub.load_state_dict_from_url(resnet18_url, model_dir=model_dir, progress=True,file_name="resnet18-f37072fd.pth")# 加载 ResNet18 并指定本地权重路径state_dict = torch.load(model_path)net = models.resnet18(weights="IMAGENET1K_V1")net.load_state_dict(state_dict)# 使用 ImageNet 预训练的 ResNet18 并修改输出类别数net.fc = torch.nn.Linear(net.fc.in_features, 10)  # 修改最后的全连接层#  定义设备device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print("使用设备:", device)#  保证两个数据不在同一个设备上net.to(device)# 3 训练网络模型# 优化3 使用更合适的优化器(如 Adam)并调整学习率。criterion = torch.nn.CrossEntropyLoss()optimizer = torch.optim.Adam(net.parameters(), lr=0.001)# 混合精度 scalerscaler = torch.amp.GradScaler('cuda')# 循环训练nums_epoch = 10  # 优化2:增加训练轮数for epoch in range(nums_epoch):net.train()running_loss = 0.0# 一次循环 50000/128=390次for i, (inputs, labels) in enumerate(train_loader, 0):inputs, labels = inputs.to(device), labels.to(device)optimizer.zero_grad()with autocast(device_type="cuda"):outputs = net(inputs)loss = criterion(outputs, labels)scaler.scale(loss).backward()scaler.step(optimizer)scaler.update()running_loss += loss.item()if i % 50 == 0:print('[%d, %5d] loss: %.3f' % (epoch, i, running_loss / 100))running_loss = 0.0print("训练结束")# 应用网络模型# 迭代测试集加速器 抽取测试集中的4张图片 每个批次包含 36 张图像(因为 batch_size=4)data_iter = iter(test_loader)# 使用 next(data_iter) 获取第一个批次的数据,其中包含 36 张图像和对应的标签。images, labels = next(data_iter)# torchvision.utils.make_grid(images) 将这 36 张图像拼接成一个网格形式,便于显示image_show(torchvision.utils.make_grid(images))# 打印图片真实分类print('图像真实分类:', ' '.join('%5s' % classes[labels[j]] for j in range(batch_size)))# 将图像images输入到模型net,得到模型输出outputs = net(images.to(device))# 获取模型预测结果 在输出的第一维(每个图像)上找到最大的值和索引 1表示在第一维上进行最大值搜索_, predicted = torch.max(outputs, 1)print('图像预测分类:', ' '.join('%5s' % classes[predicted[j]] for j in range(batch_size)))# 定义预测数量和总预测值初始化为0correct, total = 0, 0# 开启模型评估模式,关闭 BatchNorm 和 Dropoutnet.eval()# 禁用梯度计算 进行测试with torch.no_grad():# 遍历测试集的数据和对应的标签for images, labels in test_loader:# 将图像输入到模型net,得到模型输出images, labels = images.to(device), labels.to(device)outputs = net(images)# 在输出的第一维(每个图像)上找到最大的值和索引 得到预测的类别predicted_, predicted = torch.max(outputs.data, 1)# 累计总预测数量total 通过labels.size(0) 获取标签数量total += labels.size(0)# 累计预测正确的数量correct 通过判断预测的类别predicted 是否等于标签labels .sum().item将张量转化为整数correct += (predicted == labels).sum().item()# 打印测试集的准确率print('预测准确率: %d %%' % (100 * correct / total))# 保存模型torch.save(net.state_dict(), 'resnet18_cifar10.pth')

训练结果(优化版)

...
[8,     0] loss: 0.003
[8,    50] loss: 0.161
[8,   100] loss: 0.163
[8,   150] loss: 0.179
[9,     0] loss: 0.003
[9,    50] loss: 0.141
[9,   100] loss: 0.156
[9,   150] loss: 0.155
预测准确率: 81 %

存在的问题

  • 训练时gpu占用显存较小,不到2GB
  • 训练时间较长,大部分时间可能用于数据传输,GPU高负载时间较短
  • window平台无法使用 Triton加速

相关文章:

  • 黑马程序员C++2024新版笔记 第三章 数组
  • 没有 Mac,我如何用 Appuploader 完成 iOS App 上架
  • C++(25): 标准库 <deque>
  • 打卡day30
  • 二:操作系统之进程通信(IPC)
  • 大语言模型训练数据格式:Alpaca 和 ShareGPT
  • 显示器无法接受键盘/鼠标问题解决
  • C++面试2——C与C++的关系
  • 2021ICPC四川省赛个人补题ABDHKLM
  • day 29
  • web页面布局基础
  • FAL结构体分析
  • 【MySQL】存储过程,存储函数,触发器
  • GESP编程能力等级认证C++3级1-数组1
  • 一:操作系统之系统调用
  • fcQCA模糊集定性比较分析法-学习笔记
  • supervisorctl守护进程
  • DeepSeek在简历筛选系统中的深度应用
  • 【PRB】1.5w字深度解析GaN中最浅的受主缺陷
  • 缓存一致性问题与MESI协议
  • 多名幼师殴打女童被行拘后续,盘锦教育局工作人员:该局将专项整治全市幼儿园
  • 8000余万元黄金投入研发后“不知去向”,咋回事?
  • 江南考古文脉探寻
  • 公示资费套餐、规范营销行为,今年信息通信行业将办好这十件实事
  • “走进书适圈”:一周城市生活
  • 中国情怀:时代记录与家国镜相|澎湃·镜相第三届非虚构写作大赛征稿启事