PyTorch中的transforms,喂饭教程,有一点基础的都能看懂

引言 

transforms是 PyTorch 中提供的一个图像预处理模块,可以方便地对图像进行各种变换操作。

本文用pycharm进行操作

用法与代码讲解

首先导入包

from torchvision import transforms

按住ctrl点击transforms,可以看到这个

 再按住ctrl点击.transforms可以看见源码

 按Alt+7可以看结构

我们可以发现transforms中有这么多的模块,接下来,我会讲解其中一些常用的模块及其用法

1.ToTensor

源码是这样的

根据描述,可以把PIL读取的图片或ndarray(一般opencv读取图片就是这个格式)转换成torch中特有的一种数据格式——tensor(张量)。

实操

此次通过PIL读取图片

首先导入相关包

from PIL import Image

接着读取图片

path = 'train/ants_image/0013035.jpg'
img = Image.open(path)

然后用totensor将其转换成tensor格式

#totensor测试
toTensor = transforms.ToTensor()
img_tensor = toTensor(img)

具体来说,就是先实例化一个toTensor对象,然后通过这个对象将img转换成tensor格式的img

我们可以看源码

对于ToTensor类,实例化的时候不需要输入任何参数

这个__init__就类似于是构造函数,所以我们在实例化对象的时候不需要传入什么参数

toTensor = transforms.ToTensor()

toTensor(img)

本质上就是调用了__call__这个方法,根据描述,会将PIL或ndarray转换成tensor并返回。

 tips:python中class中的一些以__开头__结尾的方法都是一些内置方法,你可以通过重写这些方法来实现自定义某些功能,例如切片,就是调用__getitem__,而__repr__用于返回对象的字符串表示形式,当你print()的时候就是传给print这个方法的返回值,具体的我在这里不细讲,有兴趣可以自行搜索相关内容。

2.Normalize

源码是这样的,怎么看源码我就不再重复了 

根据源码描述,这个类用于讲tensor格式的图像进行归一化处理,并且不支持PIL格式的Image。

具体来说,它将每个通道中每个像元的像素值按照如下公式进行标准化:

output[channel] = (input[channel] - mean[channel]) / std[channel]

那么什么是归一化

归一化是指将数据按比例缩放,使之落入一个小的特定区间内。举个例子,你现在有三组数据同时进行训练,一组数据是房价,数据的大小都是几十万几百万,一组数据是房子的面积,数据的大小都是几十几百,一组数据是人口,数据大小几千万几个亿,那么不同尺度大小的数据进行训练的时候就会出现一系列的问题,需要通过归一化将这些数据缩放到同一个尺度下。

而对于图像。如果不对图像进行归一化,可能会导致深度学习模型的训练过程出现问题。

首先,深度学习模型的权重是随机初始化的,而图像像素值的范围通常是 [0, 255],这样会导致输入的图像数据的值域过大,超出了模型的初始权重所能处理的范围。为了避免这种情况,我们需要对图像进行归一化操作,将图像像素值缩放到一个较小的范围内,例如 [0, 1] 或者 [-1, 1]。这样可以避免在训练过程中出现梯度爆炸或者梯度消失的问题。

归一化有很多种方法:

常见的归一化方法包括:

  1. 最值归一化(Min-Max scaling):将所有数据缩放到 [0, 1] 的区间内。
  2. z-score 标准化(Standard scaling):通过将数据缩放到均值为 0,方差为 1 的标准正态分布上,实现对数据的标准化处理。
  3. L1 归一化:将向量调整为在所有元素的绝对值的和为 1 的单位向量。
  4. L2 归一化:将向量调整为在所有元素的平方和为 1 的单位向量。

在transforms中,Normalize就是使用的z-score标准化方法。将数据按照传入的均值和标准差进行缩放

可以看一下源码,在初始化的时候需要传入一个均值和标准差

#Normalize测试
norm = transforms.Normalize(mean = ([0.5,0.5,0.5]),std=([0.5,0.5,0.5]))

因为图像往往是三通道的,即R,G,B,所以这里我们传入的参数是一个包含三个元素的list。当然,要根据实际情况来,有些特殊的图像可能有个多通道,例如多逛如图像,或者包含红外光波段紫外光波段的图像等等图像。

然后我们和之前的toTensor一样

img_norm = norm(img_tensor)

这样就完成了归一化处理

tips:可能有些人翻遍了源码发现这个Normalize类没有重写__call__方法,怎么能直接norm()呢。对于这个问题,我们可以看见这个类他继承了神经网络模型的基类。所以直接这样用,那么基类太复杂,我在此就不多说了,我也看的不是很懂。大家知道这个归一化方法是这么用的就行。

 3.Compose

看一下源码:

根据描述,可以将多个transforms的功能组合起来 

 看一下他的例子就能简单理解了

 在你实例化这个类的时候,传入的参数是你将要对你的数据进行一个什么样的处理这样的画就不用魅族数据都写一遍处理过程,直接一个Compose对象就能一步到位。例子中就是先将输入图像进行中心裁剪transforms.CenterCrop(10) ,然后将 PIL 图像转换为 PyTorch 的张量格式transforms.PILToTensor() ,然后将图像张量的数据类型从默认的 uint8 转换为 float32,transforms.ConvertImageDtype(torch.float)。

具体操作一下:

import torch

#Compose测试
compose = transforms.Compose([
        transforms.CenterCrop(10),
        transforms.PILToTensor(),
        transforms.ConvertImageDtype(torch.float),
        ])
img_change = compose(img)

这样就处理完了

别忘了import torch,因为之前我们值导入了transforms和PIL中的Image,对于torch这个最基本的包没导入。

怎么看结果呢

我们可以用tensorboard这个torch自带的可视化工具进行查看

from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter('logs')
writer.add_image('normalize',img_norm)
writer.add_image('Compose',img_change)
writer.close()

对于tensorboard不熟悉的可以看我上一篇文章,PyTorch中的可视化工具TensorBoard_浪浪山猪的博客-CSDN博客

然后你的项目文件夹下就会出现一个tensorboard的日志,文件路径和你初始化writer时传入的参数一致。

在终端中输入命令

tensorboard --logdir=logs #其中logs是你日志文件夹的名字

会出来一个链接,点击就能查看

  tips:

1.命令中等号两边不能有空格

2.每次在tensorboard中更改之后都要重新运行并重新输入命令生成新的链接

3.不能在ps环境中输入命令,新版pycharm中默认的终端环境是ps

 4.Resize

顾名思义,用于改变图像的size(大小),这个size指的是图像的长宽大小,不是指内存大小

先看源码:

同样继承了基类 。根据描述,可以根据你传入的size改变传入图像的大小。这个图片如果是tensor类型的,将会被认为他的shape是[...,高,宽],其中...指的是维度。通常情况下,我们读取的三通道tensor格式的图片的shape是【3,高,宽】。

实操

首先查看原图像大小

#Resize测试
print(img.size)

 接下来我们将他的高改为100,也就是传入参数size = [100,512]

#Resize测试
print(img.size)
resize = transforms.Resize([100,512])
img_resize = resize(img_tensor)

看一下结果,我们需要重新输入tensorboard --logdir=logs命令并且点击新的链接,每次在tensorboard中更改图片都要重新输入命令,如果结果和你想象的不一样,最好删掉旧的日志文件。

writer = SummaryWriter('logs')
writer.add_image('normalize',img_norm)
writer.add_image('Compose',img_change)
writer.add_image('resize',img_resize)
writer.close()

tips:

1. 可能会报一个警告UserWarning: The default value of the antialias parameter of all the resizing transforms (Resize(), RandomResizedCrop(), etc.) will change from None to True in v0.17, in order to be consistent across the PIL and Tensor backends. To suppress this warning, directly pass antialias=True (recommended, future default), antialias=None (current default, which means False for Tensors and True for PIL), or antialias=False (only works on Tensors - PIL will still use antialiasing). This also applies if you are using the inference transforms from the models weights: update the call to weights.transforms(antialias=True).
  warnings.warn( 

这个不用管他,它提示你在下一个版本中(v0.17)transforms 默认的 antialias 参数值将从 None 更改为 True,以使 PIL 和 Tensor 后端保持一致。这个是基类中某个参数的默认值在下个版本中要改了。

2. 如果你传入的size参数不是一个二元列表,而是一个值,比如说100,那么他会改变哪个更短的一边的size,例如例子size是(768,512),如果我的size传入100,那么它会将短的一边,即长512个像素点的拿一条边改成100。

这点在源码中有写,如果size是一个int,会有一个判断,改变短的那边

 结束语

本文介绍了一些transforms中的一些最常用的类,全部讲的话太多了,就讲一些最常用的。transforms中还有很多有趣的小工具,用法和这个基本都一致,都是实例化一个对象然后通过 对象名(图像)的方法进行调用。就是实例化的时候传入的参数不同。真要用到的话可以直接看源码中的讲解,也可以看一下别人的贴子。

猜你喜欢

转载自blog.csdn.net/m0_50317149/article/details/130812328