PyTorch08-PyTorch生态简介
八、PyTorch生态简介
前面几个章节已经学习了张量的基础知识、PyTorch
模型的常用组成部分与定义、模型的常规训练与验证/测试流程、模型可视化等内容,大家对基于
PyTorch
的深度学习已经有了初步的认知。回顾一下,前面学习过程中使用到的场景、示例大多是计算机视觉(CV)相关的,几乎一直在使用
PyTorch 官方开源的 torchvision
这个计算机视觉包。虽然一直在使用,但并未从整体上对
torchvision
做过一个完整系统的总结,这正是本章节要做的事情之一。
此外,PyTorch 自 2016 年由 Meta 公司(原 Facebook)开源并发布首个 Alpha 版本以来,迅速流行并逐渐确立了主流地位。一方面得益于 Meta 强大的推广与 PyTorch 框架本身极佳的设计与易用性,而另一个核心原因则是 PyTorch 生态的包容性与完整性。
在 PyTorch
首个开源版本发布后,官方针对不同的垂直领域(视觉、文本、音频、视频)先后推出了四大核心扩展库:torchvision、torchtext、torchaudio
和
pytorchvideo。这些核心扩展库的推出极大地便利了相关应用的开发,并有力地促进了
PyTorch 生态的繁荣与推广。
本章节学习的主要内容正是:对
torchvision、torchtext、torchaudio
和 pytorchvideo
这几个核心扩展库进行系统性的总结与介绍。
每个扩展核心库的内容较多,此处只会对针对其中主流常用或具有代表性的子包或工具进行阐述,要继续深入研究其中某个子包的功能或特性,可以参考其官方文档展开。
8.1 torchvision介绍
torchvision是PyTorch官方开源的计算机视觉扩展库,其中提供了当前研究与开发中常用的与计算机视觉处理相关的功能:图像数据集加载(如
MNIST、CIFAR、ImageNet)、经典模型架构(ResNet、VGG
等)以及丰富的图像变换与数据增强工具(transforms),此外其还提供了
解码/编码图像与视频(io)、运算符/损失函数和层(ops)、
各种实用工具程序(utils)等相关子包及功能。
torchvision的官方文档:https://docs.pytorch.org/vision/0.21/,其中文版本的镜像化文档:https://pytorch.ac.cn/vision/0.21/。
本章节将按照如下顺序进行展开: 1
2
3
4
5
6图像数据集加载torchvision.datasets
经典模型架构torchvision.models
图像变换与数据增强torchvision.transforms
解码/编码图像与视频torchvision.io
运算符/损失函数和层torchvision.ops
各种实用工具程序torchvision.utils
8.1.1 图像数据集加载torchvision.datasets
官方文档地址:torchvision.datasets 文档 。
8.1.1.1 内置数据集
Torchvision 在 torchvision.datasets
模块中提供了许多内置数据集,以及用于构建自定义数据集的实用工具类。
所有数据集都是 torch.utils.data.Dataset
的子类,即它们都实现了 __getitem__ 和 __len__
方法。因此,它们都可以传递给
torch.utils.data.DataLoader。
1 | |
所有数据集都有几乎相似的
API。它们都有两个共同的参数:transform 和
target_transform,分别用于转换输入和目标。
具体的数据集列表见:torchvision 0.21.0中内置数据集 。
8.1.1.2 自定义数据集类
还可以使用PyTorch提供的基类创建自己的数据集。官方提供了如下几个基类,供自定义数据集类时继承。
| 基类 | 简介 |
|---|---|
DatasetFolder(root, loader[, extensions, ...]) |
A generic data loader. |
ImageFolder(root, ~pathlib.Path], transform, ...) |
A generic data loader where the images are arranged in this way by default: . |
VisionDataset([root, transforms, transform, ...]) |
Base Class For making datasets which are compatible with torchvision. |
自定义数据集类时,需要重写如下几个方法:__getitem__、__len__,根据重写__init__方法。
8.1.1.3 wrap_dataset_for_transforms_v2
在 PyTorch 中,可以使用 wrap_dataset_for_transforms_v2
适配新版数据增强。
在 PyTorch 视觉库的演进中,torchvision.transforms.v2
引入了极其强大的联合数据增强能力(即可以同时对图像、目标检测框
boxes、分割掩码 masks 以及关键点
keypoints
进行同步的空间几何变换,如随机裁剪、水平翻转等)。
然而,许多传统或经典的数据集(例如
CocoDetection)在设计时,其返回的标签格式是原生的 Python
列表或字典,无法直接被现代的 v2
管道所识别。为此,官方提供了
wrap_dataset_for_transforms_v2
工具来无缝解决这一兼容性问题。
1 | |
8.1.2 经典模型架构torchvision.models
官方文档地址:torchvision.models官方文档。
torchvision.models
子包包含用于解决不同任务的模型定义,包括:图像分类、像素级语义分割、目标检测、实例分割、人体关键点检测、视频分类和光流(针对这些不同类型的模型,torchvision.datasets中提供了对应的内置数据集)。
为了方便使用者,PyTorch官方已经在torchvision.models
子包中收纳了许多module与model,具体可参考: torchvision/models
,也可以在源码文件torchvision/models/__init__.py中查看,可直接下载。同时为每个model也提供了多个可选的预训练权重,可供直接下载。
8.1.2.1 模型的预训练权重
初始化预训练模型 从 v0.13 开始,TorchVision 为现有的模型构建器方法提供了新的多权重支持 API,用于加载不同的权重。
1
2
3
4
5
6
7
8
9
10
11
12
13from torchvision.models import resnet50, ResNet50_Weights
# Using pretrained weights:
resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)
resnet50(weights="IMAGENET1K_V1")
resnet50(pretrained=True) # deprecated
resnet50(True) # deprecated
# Using no weights:
resnet50(weights=None)
resnet50()
resnet50(pretrained=False) # deprecated
resnet50(False) # deprecated使用预训练模型 在使用预训练模型之前,必须预处理图像(使用正确的分辨率/插值调整大小,应用推理变换,重新缩放值等)。没有标准的方法来执行此操作,因为它取决于给定模型的训练方式。它可能因模型系列、变体甚至权重版本而异。使用正确的预处理方法至关重要,否则可能会导致准确性降低或输出不正确。
每个预训练模型的推理变换的所有必要信息都在其权重文档中提供。为了简化推理,TorchVision
将必要的预处理变换捆绑到每个模型权重中。这些可以通过
weight.transforms 属性访问: 1
2
3
4
5
6# Initialize the Weight Transforms
weights = ResNet50_Weights.DEFAULT
preprocess = weights.transforms()
# Apply it to the input image
img_transformed = preprocess(img)
- 列出和检索可用模型 从 v0.14 开始,TorchVision
提供了一种新机制,允许按名称列出和检索模型和权重。以下是如何使用它们的一些示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96# 导入 torchvision 顶层包;后面用它访问 torchvision.models.quantization 中的模型构造函数。
import torchvision
# 导入模型注册表相关函数:列出名称、按名称创建模型、查询模型可用权重、按字符串取得具体权重。
from torchvision.models import list_models, get_model, get_model_weights, get_weight
# 导入量化 MobileNet V3 Large 对应的权重枚举类,用于和查询结果进行比较。
from torchvision.models.quantization import MobileNet_V3_Large_QuantizedWeights
# 列出 torchvision 全部已注册模型的名称,返回由字符串组成的列表。
# 这里只保存结果而不打印;可使用 print(all_models) 查看具体模型名称。
all_models = list_models()
"""
all_models 的值如下:
['alexnet', 'convnext_base', 'convnext_large', 'convnext_small', 'convnext_tiny', 'deeplabv3_mobilenet_v3_large',
'deeplabv3_resnet101', 'deeplabv3_resnet50', 'densenet121', 'densenet161', 'densenet169', 'densenet201',
'efficientnet_b0', 'efficientnet_b1', 'efficientnet_b2', 'efficientnet_b3', 'efficientnet_b4', 'efficientnet_b5',
'efficientnet_b6', 'efficientnet_b7', 'efficientnet_v2_l', 'efficientnet_v2_m', 'efficientnet_v2_s',
'fasterrcnn_mobilenet_v3_large_320_fpn', 'fasterrcnn_mobilenet_v3_large_fpn', 'fasterrcnn_resnet50_fpn',
'fasterrcnn_resnet50_fpn_v2', 'fcn_resnet101', 'fcn_resnet50', 'fcos_resnet50_fpn', 'googlenet', 'inception_v3',
'keypointrcnn_resnet50_fpn', 'lraspp_mobilenet_v3_large', 'maskrcnn_resnet50_fpn', 'maskrcnn_resnet50_fpn_v2',
'maxvit_t', 'mc3_18', 'mnasnet0_5', 'mnasnet0_75', 'mnasnet1_0', 'mnasnet1_3', 'mobilenet_v2', 'mobilenet_v3_large',
'mobilenet_v3_small', 'mvit_v1_b', 'mvit_v2_s', 'quantized_googlenet', 'quantized_inception_v3',
'quantized_mobilenet_v2', 'quantized_mobilenet_v3_large', 'quantized_resnet18', 'quantized_resnet50',
'quantized_resnext101_32x8d', 'quantized_resnext101_64x4d', 'quantized_shufflenet_v2_x0_5',
'quantized_shufflenet_v2_x1_0', 'quantized_shufflenet_v2_x1_5', 'quantized_shufflenet_v2_x2_0',
'r2plus1d_18', 'r3d_18', 'raft_large', 'raft_small', 'regnet_x_16gf', 'regnet_x_1_6gf', 'regnet_x_32gf',
'regnet_x_3_2gf', 'regnet_x_400mf', 'regnet_x_800mf', 'regnet_x_8gf', 'regnet_y_128gf', 'regnet_y_16gf',
'regnet_y_1_6gf', 'regnet_y_32gf', 'regnet_y_3_2gf', 'regnet_y_400mf', 'regnet_y_800mf', 'regnet_y_8gf',
'resnet101', 'resnet152', 'resnet18', 'resnet34', 'resnet50', 'resnext101_32x8d', 'resnext101_64x4d',
'resnext50_32x4d', 'retinanet_resnet50_fpn', 'retinanet_resnet50_fpn_v2', 's3d', 'shufflenet_v2_x0_5',
'shufflenet_v2_x1_0', 'shufflenet_v2_x1_5', 'shufflenet_v2_x2_0', 'squeezenet1_0', 'squeezenet1_1',
'ssd300_vgg16', 'ssdlite320_mobilenet_v3_large', 'swin3d_b', 'swin3d_s', 'swin3d_t', 'swin_b', 'swin_s',
'swin_t', 'swin_v2_b', 'swin_v2_s', 'swin_v2_t', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn',
'vgg19', 'vgg19_bn', 'vit_b_16', 'vit_b_32', 'vit_h_14', 'vit_l_16', 'vit_l_32', 'wide_resnet101_2',
'wide_resnet50_2']
"""
# 只列出 torchvision.models 模块中的模型名称,主要是常用的非量化视觉分类模型。
# 相比 all_models,这个列表不包含注册在其他子模块(如 quantization)中的模型。
classification_models = list_models(module=torchvision.models)
"""
classification_models 的值如下:
['alexnet', 'convnext_base', 'convnext_large', 'convnext_small', 'convnext_tiny', 'densenet121', 'densenet161',
'densenet169', 'densenet201', 'efficientnet_b0', 'efficientnet_b1', 'efficientnet_b2', 'efficientnet_b3',
'efficientnet_b4', 'efficientnet_b5', 'efficientnet_b6', 'efficientnet_b7', 'efficientnet_v2_l',
'efficientnet_v2_m', 'efficientnet_v2_s', 'googlenet', 'inception_v3', 'maxvit_t', 'mnasnet0_5',
'mnasnet0_75', 'mnasnet1_0', 'mnasnet1_3', 'mobilenet_v2', 'mobilenet_v3_large', 'mobilenet_v3_small',
'regnet_x_16gf', 'regnet_x_1_6gf', 'regnet_x_32gf', 'regnet_x_3_2gf', 'regnet_x_400mf', 'regnet_x_800mf',
'regnet_x_8gf', 'regnet_y_128gf', 'regnet_y_16gf', 'regnet_y_1_6gf', 'regnet_y_32gf', 'regnet_y_3_2gf',
'regnet_y_400mf', 'regnet_y_800mf', 'regnet_y_8gf', 'resnet101', 'resnet152', 'resnet18', 'resnet34',
'resnet50', 'resnext101_32x8d', 'resnext101_64x4d', 'resnext50_32x4d', 'shufflenet_v2_x0_5',
'shufflenet_v2_x1_0', 'shufflenet_v2_x1_5', 'shufflenet_v2_x2_0', 'squeezenet1_0', 'squeezenet1_1',
'swin_b', 'swin_s', 'swin_t', 'swin_v2_b', 'swin_v2_s', 'swin_v2_t', 'vgg11', 'vgg11_bn', 'vgg13',
'vgg13_bn', 'vgg16', 'vgg16_bn', 'vgg19', 'vgg19_bn', 'vit_b_16', 'vit_b_32', 'vit_h_14', 'vit_l_16',
'vit_l_32', 'wide_resnet101_2', 'wide_resnet50_2']
"""
# 依据字符串名称从模型注册表创建普通的 MobileNet V3 Large。
# weights=None 表示只创建随机初始化的网络结构,不下载也不加载预训练参数。
m1 = get_model("mobilenet_v3_large", weights=None)
# 按名称创建量化版本的 MobileNet V3 Large,并加载该模型的默认预训练权重。
# "DEFAULT" 是该模型推荐默认权重的别名;第一次运行若本地缓存不存在,会自动下载权重文件。
m2 = get_model("quantized_mobilenet_v3_large", weights="DEFAULT")
# m1、m2 都是 MobileNet V3 Large 的实例,但 m1 是普通模型,m2 是量化模型,且 m2 已加载预训练权重。
# 通过“权重枚举类名.成员名”这一字符串取得一个具体权重配置对象。
# 该对象除了指向预训练参数外,还保存与此权重匹配的预处理和类别等元数据。
weights = get_weight("MobileNet_V3_Large_QuantizedWeights.DEFAULT")
# weights 的值输出成字符串: MobileNet_V3_Large_QuantizedWeights.IMAGENET1K_QNNPACK_V1
# 断言上一步按字符串得到的对象,正是直接访问枚举成员得到的 DEFAULT 权重对象。
# 若两者不相等,assert 会抛出 AssertionError,说明查询名称或当前 torchvision 注册信息有问题。
assert weights == MobileNet_V3_Large_QuantizedWeights.DEFAULT
# 根据模型注册名查询“这个模型有哪些可用权重”,返回权重枚举类本身,而非某一份具体权重。
# 也不是 多个具体权重对象组成的列表。它相当于“这个模型可选权重版本的目录”。这个目录可以包含一个或多个具体权重成员。
# 查看当前 quantized_mobilenet_v3_large 的枚举成员:
# list(weights_enum.__members__)
# ['IMAGENET1K_QNNPACK_V1', 'DEFAULT']
weights_enum = get_model_weights("quantized_mobilenet_v3_large")
# weights_enum 的值输出成字符串: <enum 'MobileNet_V3_Large_QuantizedWeights'>
# 验证按模型名称查到的权重枚举类,就是前面导入的 MobileNet_V3_Large_QuantizedWeights。
assert weights_enum == MobileNet_V3_Large_QuantizedWeights
# get_model_weights() 也可以直接接收模型构造函数,而不只接收字符串名称。
# 此处从 torchvision.models.quantization 命名空间取出 quantized_mobilenet_v3_large 构造函数。
weights_enum2 = get_model_weights(torchvision.models.quantization.mobilenet_v3_large)
# weights_enum2 的值输出成字符串: <enum 'MobileNet_V3_Large_QuantizedWeights'>
# 验证“传模型名称”和“传模型构造函数”两种查询方式得到的是同一个权重枚举类。
assert weights_enum == weights_enum2
以下是检索模型及其对应权重的可用公共函数:
| 方法 | 功能 |
|---|---|
get_model(name, **config) |
获取模型名称和配置,并返回实例化的模型。 |
get_model_weights(name) |
返回与给定模型关联的权重枚举类。 |
get_weight(name) |
按其全名获取权重枚举值。 |
list_models([module, include, exclude]) |
返回包含已注册模型名称的列表。 |
- 使用 Hub 中的模型 大多数预训练模型可以直接通过 PyTorch Hub
访问,而无需安装 TorchVision:
1
2
3
4
5
6
7
8
9
10
11
12
13
14import torch
# 大多数预训练模型可以直接通过 PyTorch Hub 访问,而无需安装 TorchVision
# Option 1: passing weights param as string
model = torch.hub.load("pytorch/vision", "resnet50", weights="IMAGENET1K_V2")
# Option 2: passing weights param as enum
weights = torch.hub.load("pytorch/vision", "get_weight", weights="ResNet50_Weights.IMAGENET1K_V2")
model = torch.hub.load("pytorch/vision", "resnet50", weights=weights)
# 还可以通过 PyTorch Hub 检索特定模型的所有可用权重,方法是执行以下操作
weight_enum = torch.hub.load("pytorch/vision", "get_model_weights", name="resnet50")
print([weight for weight in weight_enum])
8.1.2.2 模型可分成几类
8.1.2.2.1 分类模型
官方文档:https://docs.pytorch.org/vision/0.21/models.html#classification。
PyTorch官方提供了如下这些分类模型,绝大部分包含预训练权重参数: - AlexNet
- ConvNeXt
- DenseNet
- EfficientNet
- EfficientNetV2
- GoogLeNet
- Inception
V3 - MaxVit
- MNASNet
- MobileNet
V2 - MobileNet
V3 - RegNet
- ResNet
- ResNeXt
- ShuffleNet
V2 - SqueezeNet
- SwinTransformer
- VGG
- VisionTransformer
- Wide
ResNet 以下是一个使用预训练图像分类模型的示例: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24from torchvision.io import decode_image
from torchvision.models import resnet50, ResNet50_Weights
img = decode_image("test/assets/encode_jpeg/grace_hopper_517x606.jpg")
# Step 1: Initialize model with the best available weights
weights = ResNet50_Weights.DEFAULT
model = resnet50(weights=weights)
model.eval()
# Step 2: Initialize the inference transforms
preprocess = weights.transforms()
# Step 3: Apply inference preprocessing transforms
batch = preprocess(img).unsqueeze(0)
# Step 4: Use the model and print the predicted category
prediction = model(batch).squeeze(0).softmax(0)
class_id = prediction.argmax().item()
score = prediction[class_id].item()
category_name = weights.meta["categories"][class_id]
# 计算类别名称与对应的得分(准确率)
print(f"{category_name}: {100 * score:.1f}%")
# 预训练模型输出的所有分类名与序号可以通过 weights.meta["categories"] 查看
torchvision 中在ImageNet-1K 数据集(拥有约 128 万张图片、1000 个类别)训练得到的一批模型与对应权重参数,在 ImageNet-1K 上使用单裁剪进行测试后得到的测试报告结果可参考:所有可用分类权重的表格 。其中Weight(权重名称) 表示特定模型及其预训练权重版本的枚举名称,Acc@1(Top-1 准确率)、Acc@5(Top-5 准确率) 分别表示概率最大的预测分类的正确率、概率最大的前5个预测分类中包含正确类别的概率,Params(参数量)表示模型的总参数数量,GFLOPS(计算复杂度)表示每秒浮点运算次数(Giga Floating-point Operations Per Second)。
当需要挑选模型时,可以通过它直观地对比不同模型在准确率、参数量(体积)和计算量(速度)之间的权衡,从而根据自己的硬件条件(如服务器显卡配置)和业务需求选择最合适的网络结构。
此外,torchvision中如下有一部分构架提供了INT8 量化模型,可参考:INT8 量化模型 。
8.1.2.2.2 语义分割
官方文档:https://docs.pytorch.org/vision/0.21/models.html#semantic-segmentation。
torchvision 中目前提供了如下三个语义分割模型: - DeepLabV3 - FCN - LRASPP
torchvision 中还提供了所有可用语义分割权重的表格,其中所有模型都在 COCO val2017 的子集上进行评估,该子集包含 Pascal VOC 数据集中存在的 20 个类别。详情见:所有可用语义分割权重的表格 。 其中20 个类别分别是:aeroplane(飞机)、bicycle(自行车)、bird(鸟)、boat(船)、bottle(瓶子)、bus(公共汽车)、car(汽车)、cat(猫)、chair(椅子)、cow(牛)、diningtable(餐桌)、dog(狗)、horse(马)、motorbike(摩托车)、person(人)、pottedplant(盆栽)、sheep(羊)、sofa(沙发)、train(火车)、tvmonitor(电视/显示器)。再加上background(背景)。
8.1.2.2.3 目标检测、实例分割和人体关键点检测
官方文档:https://docs.pytorch.org/vision/0.21/models.html#object-detection-instance-segmentation-and-person-keypoint-detection。
用于检测、实例分割和关键点检测的预训练模型使用 torchvision 中的分类模型进行初始化。
torchvision 中提供了几个目标检测模型: - Faster R-CNN - FCOS - RetinaNet - SSD - SSDlite
torchvision 中目前只提供了一个实例分割模型: - Mask R-CNN
torchvision 中目前只提供了一个人体关键点检测模型: - Keypoint R-CNN
8.1.2.2.4 视频分类
官方文档:https://docs.pytorch.org/vision/0.21/models.html#video-classification。
torchvision 在 Kinetics-400 数据集基础训练后,提供了如下几个视频分类模型: - Video MViT - Video ResNet - Video S3D - Video SwinTransformer
8.1.3 图像变换与数据增强torchvision.transforms
官方文档:https://docs.pytorch.org/vision/0.21/transforms.html。
torchvision.transforms 是 PyTorch
中用于计算机视觉任务的核心模块。虽然它常被称为数据增强工具包,但严谨地说,其提供的图像变换操作主要包含两大核心类别:
基础预处理 / 数据清洗(Preprocessing)
- 操作:如将图像转换为 Tensor 并归一化、调整尺寸等。
- 主要目的:这是让数据“符合模型胃口”的必要步骤,它不改变数据的多样性,通常在训练集、验证集和测试集中均会使用。
狭义的数据增强(Data Augmentation)
- 操作:如随机水平翻转、随机裁剪、色彩抖动等。
- 主要目的:专门用于训练集,通过不断制造视觉“扰动”来扩充样本空间、防止模型过拟合,从而有效提升模型在未知场景下的泛化能力(注意:测试集通常绝对不能使用这类随机增强操作)。
- 操作:如随机水平翻转、随机裁剪、色彩抖动等。
通过组合各类图像变换手段,该模块不仅能将原始图像转换为模型所需的格式,还能支持图像与标注(如检测框、分割掩码)的同步几何变换,从而有效丰富训练数据并提升模型的泛化能力。 备注:数据增强 只是从其最终使用场景与目的来说的,其底层执行的操作仍然是图像变换。
由于版本迭代更新,PyTorch其实为torchvision中数据变换与数据增强提供了两个版本的API:V1(torchvision.transforms)、V2(torchvision.transforms.v2)。V2
API是于2023年初发布的torchvision 0.15中首次引入的,提供了更多的功能且运行更快,是PyTorch官方推荐的现代版本。由于历史原因,网络上和各个开源项目中仍存在大量使用
V1 的代码,新版 torchvision 仍对其提供支持(就是说,即使是最新的
torchvision 0.29
也未将torchvision.transforms子包删除)。
以下代码片段展示了利用torchvision.transforms.v2进行图像变换的核心过程。在
第六章6.6小节
数据增强 也有相关描述。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51import torch # 提供 float32 数据类型,供 ToDtype 指定输出 Tensor 的类型。
from PIL import Image # Image.open() 用于从磁盘读取图片,返回 PIL.Image 对象。
from torchvision.transforms import v2 # torchvision 0.21 的新版图像变换 API。
# v2.Compose 会按照列表从上到下的顺序依次执行每一个变换。
# 训练集使用随机变换:同一张图片被多次读取时,裁剪区域和是否翻转都可能不同。
train_transform = v2.Compose([
# 将 PIL 图片转换为 torchvision 的图像 Tensor 表示,通道顺序变为 (C, H, W)。
# 此时像素通常仍是 uint8,数值范围仍是 [0, 255],尚未归一化。
v2.ToImage(), # 结果形状与数据类型:torch.Size([3, 1280, 1918]) torch.uint8
# 从原图随机选择一个区域,再把该区域缩放成 224×224。
# size 是最终输出的 (高度, 宽度),224×224 是许多 ImageNet 预训练分类模型的常见输入尺寸。
# scale=(0.8, 1.0) 表示随机裁取原图面积的 80%~100%,可模拟物体在画面中的不同大小和位置。
# antialias=True 在缩小图片时使用抗锯齿,减少缩放带来的锯齿与高频伪影。
v2.RandomResizedCrop(
size=(224, 224),
scale=(0.8, 1.0),
antialias=True,
), # 结果形状与数据类型:torch.Size([3, 224, 224]) torch.uint8
# 以 p=0.5(50%)的概率(剩余50%的图片保持原样)沿左右方向翻转图片(水平镜像翻转,就像照镜子一样)。
# 对“汽车类别”等左右翻转不改变标签含义的自然图像分类任务通常合理;
# 但文字识别、交通标志方向识别等任务不一定适用。
v2.RandomHorizontalFlip(p=0.5), # 结果形状与数据类型:torch.Size([3, 224, 224]) torch.uint8
# 把 uint8 的 [0, 255] 像素转换为 float32 的 [0, 1]。
# scale=True 很重要:它除以 255;若省略,转换 dtype 后数值仍会是 [0, 255]。
v2.ToDtype(torch.float32, scale=True), # 结果形状与数据类型:torch.Size([3, 224, 224]) torch.float32
# 对 RGB 三个通道分别进行标准化,计算公式为:
# output[channel] = (input[channel] - mean[channel]) / std[channel]。
# 下面的均值和标准差来自 ImageNet;使用 ImageNet 预训练权重微调时应保持一致。
# 具体做法:对图片的 RGB 三个通道分别做标准化,让输入数据的分布与 ImageNet 预训练模型训练时看到的数据分布一致。
# 新值 = (原值 - 该通道均值) / 该通道标准差
v2.Normalize(
mean=(0.485, 0.456, 0.406),
std=(0.229, 0.224, 0.225),
), # 结果形状与数据类型:torch.Size([3, 224, 224]) torch.float32
])
# Image.open() 从当前工作目录读取训练图片,初始结果可能是 RGB、RGBA、灰度等不同模式。
# convert("RGB") 统一转换为 3 通道 RGB,避免灰度或带透明通道的图片与模型输入不匹配。
# 此处 ./Audi奥迪-0cdf5b5d0ce1_01.jpg 的颜色模型本来就是RGB,此图片来自如下网址
# https://www.kaggle.com/competitions/carvana-image-masking-challenge/data?select=train_hq.zip
image = Image.open("./Audi奥迪-0cdf5b5d0ce1_01.jpg").convert("RGB")
# 将训练图片依次送入 train_transform 中的五类变换,得到可直接输入分类模型的 Tensor。
# 因为包含随机裁剪和随机翻转,重复运行本行时 train_tensor 的具体像素通常会不同。
train_tensor = train_transform(image)
8.1.4 解码/编码图像与视频torchvision.io
官方文档地址:https://docs.pytorch.org/vision/0.21/io.html。
torchvision 0.21
中,torchvision.io提供了视频、图片文件的 IO
操作的功能,包括读取、写入、编解码处理操作。比如图像解码(decode_image)、图像编码(encode_png)、读写文件(read_file、write_file),有不同
API 支持视频解码,但提醒用户torchvision.io中视频解码功能后续将
移到另一个独立的开源项目 torchcodec
,且这个动作最终在 torchvision 0.29 中正式落地。
TorchCodec 是一个基于 PyTorch 的原生库,可以用来做视频、音频、图像的解码与编码操作,在CPU与NVIDIA GPU上都能用。此项开源项目的旨在提供更快、更易用的相关操作,并与PyTorch生态无缝集成。
以下是通过decode_image方法对RGB图像文件进行解码的示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28from torchvision.io import decode_image
from pathlib import Path
import torch
# 方式1: 直接给 decode_image 传入图像文件路径
path_to_image = Path("../data/carvana-image-masking-challenge-data/imgs/0cdf5b5d0ce1_01.jpg")
img = decode_image(path_to_image, mode="RGB")
print(img.dtype) # torch.uint8
# 方式2: 先以二进制文件打开读取得到字节,然后转成张量,最后传给 decode_image 方法
# 以二进制模式读取 JPEG 文件,得到 Python bytes。
with open(path_to_image, "rb") as f:
raw_encoded_bytes = f.read()
# 将 JPEG 原始字节转换为一维 uint8 Tensor。
# bytearray() 创建可写缓冲区,可避免 torch.frombuffer() 的只读缓冲区警告。
encoded_tensor = torch.frombuffer(
bytearray(raw_encoded_bytes),
dtype=torch.uint8,
)
# decode_image() 将 JPEG 压缩字节解码为图像 Tensor。
# 输出形状为 (C, H, W),本图实际为 (3, 1280, 1918)。
img = decode_image(encoded_tensor, mode="RGB")
print(img.dtype) # torch.uint8
print(img.shape) # torch.Size([3, 1280, 1918])
8.1.5 运算符/损失函数和层torchvision.ops
官方文档地址:https://docs.pytorch.org/vision/0.21/ops.html。
torchvision.ops
实现了计算机视觉特定的运算符、损失函数和层。所有运算符都原生支持
TorchScript。
它是 PyTorch
中专门为计算机视觉任务提供的高效、专有运算操作模块。它包含了许多底层且计算密集的算子(如非极大值抑制
nms、RoI 池化与对齐
roi_align、roi_pool 等),并支持 GPU
加速,是构建目标检测、实例分割等高级视觉模型不可或缺的核心工具包。
8.1.6 各种实用工具程序torchvision.utils
官方文档地址:https://docs.pytorch.org/vision/0.21/utils.html。
torchvision.utils 是 PyTorch
中用于计算机视觉任务的实用工具模块。它主要提供了一系列辅助性功能(如图像网格拼接
make_grid、将图像张量保存为文件的 save_image
等),常用于模型训练过程中的可视化、调试以及结果输出,极大地简化了图像数据的展示与保存流程。
当前提供了如下工具类方法:
| 方法 | 功能 |
|---|---|
draw_bounding_boxes(image, boxes[, labels, ...]) |
Draws bounding boxes on given RGB image. |
draw_segmentation_masks(image, masks[, ...]) |
Draws segmentation masks on given RGB image. |
draw_keypoints(image, keypoints[, ...]) |
Draws Keypoints on given RGB image. |
flow_to_image(flow) |
Converts a flow to an RGB image. |
make_grid(tensor[, nrow, padding, ...]) |
Make a grid of images. |
save_image(tensor, fp[, format]) |
Save a given Tensor into an image file. |