Pytorch学习记录[1]
本文内容包括: Dataset类、Tensorboard的使用、几种常见的Transforms
项目全源码及数据集:http://netdisk.scutvk.cn/pytorch_lesson_1.zip
P7:Dataset类代码实战
此处用到的数据集结构如图1

源码如下:
from torch.utils.data import Dataset
from PIL import Image
import os
class MyData(Dataset):#继承Dataset
def __init__(self, root_dir, label_dir):#构造函数
self.root_dir = root_dir#数据集根目录
self.label_dir = label_dir#数据集文件夹中的分类文件夹
self.path = os.path.join(self.root_dir, self.label_dir)#合并读出总的图片文件夹地址
self.image_list = os.listdir(self.path)
def __getitem__(self, index):#可以在其他地方用[i]
img_name = self.image_list[index]
img_item_path = os.path.join(self.path, img_name)#得到图片绝对地址
img = Image.open(img_item_path)
label = self.label_dir
return img, label
def __len__(self):#可以用len()
return len(self.image_list)
root_dir = "hymenoptera_data\\train"
ants_dataset = MyData(root_dir, "ants")
bees_dataset = MyData(root_dir, "bees")
dataset = ants_dataset + bees_dataset
print(len(dataset))
img, label = dataset[244]
img.show()
本集get:
(1):Python类继承
(2):__init__ __getitem__ __len__作用
(3):os.listdir() #返回包含文件夹内所有文件名的一个list os — 多种操作系统接口 — Python 3.10.0 文档
返回一个包含由 path 指定目录中条目名称组成的列表。 该列表按任意顺序排列,并且不包括特殊条目 '.'
和 '..'
,即使它们存在于目录中。 如果有文件在调用此函数期间在被移除或添加到目录中,是否要包括该文件的名称并没有规定。
path 可以是 类路径对象。如果 path 是(直接传入或通过 PathLike
接口间接传入) bytes
类型,则返回的文件名也将是 bytes
类型,其他情况下是 str
类型。
本函数也支持 指定文件描述符为参数,其中描述符必须指向目录。
引发一个 审计事件 os.listdir
,附带参数 path
。
(4):os.path.join() #返回两个路径合并 os.path — 常用路径操作 — Python 3.8.12 文档
智能地拼接一个或多个路径部分。 返回值是 path 和 *paths 的所有成员的拼接,其中每个非空部分后面都紧跟一个目录分隔符,最后一个部分除外,这意味着如果最后一个部分为空,则结果将以分隔符结尾。 如果某个部分为绝对路径,则之前的所有部分会被丢弃并从绝对路径部分开始继续拼接。
在 Windows 上,遇到绝对路径部分(例如 r'\foo'
)时,不会重置盘符。如果某部分路径包含盘符,则会丢弃所有先前的部分,并重置盘符。请注意,由于每个驱动器都有一个“当前目录”,所以 os.path.join("c:", "foo")
表示驱动器 C:
上当前目录的相对路径 (c:foo
),而不是 c:\foo
。
在 3.6 版更改: 接受一个 类路径对象 用于 path 和 paths 。
P8:Tensorboard的使用
add_scalar() 画图
python源码如下
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("logs") #构造函数指定SummaryWriter的储存文件夹
for i in range(100):
writer.add_scalar("y = 2x", 2*i, i) #将点一个个标上去到"y=2x"这个标题(tag)的图里面
writer.close()
终端命令
tensorboard --logdir=logs --port=6007 #port默认端口为6006
本集get:
(1)在Pycharm里面,按住ctrl键后,鼠标点相应的地方,会弹出帮助文档
(2) writer.add_scalar(“y = 2x”, 2*i, i) #画点的时候 2*i是y i是x
(3)若想画不同的图,一可以指定不同的logdir文件夹,二可以该表标量图的tag图名,若都不改变,则会在原来那里加上新画的点,造成错误,此时可以删掉logdir里面的数据重新画图。


add_image() 加图片上去
python源码如下:
from torch.utils.tensorboard import SummaryWriter
from PIL import Image
import numpy as np
writer = SummaryWriter("logs")
image_path = "data/train/ants_image/424119020_6d57481dab.jpg"
img_PIL = Image.open(image_path)
img_array = np.array(img_PIL)
print(img_array.shape)
writer.add_image("test", img_array, 100, dataformats="HWC")
writer.close()
add_image()帮助文档部分如下: #add_image()帮助文档链接Ubuntu Pastebin
Args: tag (string): Data identifier img_tensor (torch.Tensor, numpy.array, or string/blobname): Image data global_step (int): Global step value to record walltime (float): Optional override default walltime (time.time()) seconds after epoch of event
tag 还是我们的表示命名
img_tensor 支持的格式有 (torch.Tensor, numpy.array, or string/blobname)
global_step 是图片打标的顺序号,效果如图4
walltime 暂时看不懂是干嘛的

Shape:
img_tensor: Default is :math:`(3, H, W)`. You can use ``torchvision.utils.make_grid()`` to
convert a batch of tensor into 3xHxW format or call ``add_images`` and let us do the job.
Tensor with :math:`(1, H, W)`, :math:`(H, W)`, :math:`(H, W, 3)` is also suitable as long as
corresponding ``dataformats`` argument is passed, e.g. ``CHW``, ``HWC``, ``HW``.
img_tensor默认处理的矩阵数据结构为(通道C、高度H、宽度W),而我们使用PIL Image构造的numpy array的结构是(HWC),在后面要加上 dataformats="HWC"
说明,帮助文档中也给我们列出了样例
Examples::
from torch.utils.tensorboard import SummaryWriter
import numpy as np
img = np.zeros((3, 100, 100))
img[0] = np.arange(0, 10000).reshape(100, 100) / 10000
img[1] = 1 - np.arange(0, 10000).reshape(100, 100) / 10000
img_HWC = np.zeros((100, 100, 3))
img_HWC[:, :, 0] = np.arange(0, 10000).reshape(100, 100) / 10000
img_HWC[:, :, 1] = 1 - np.arange(0, 10000).reshape(100, 100) / 10000
writer = SummaryWriter()
writer.add_image('my_image', img, 0)
# If you have non-default dimension setting, set the dataformats argument.
writer.add_image('my_image_HWC', img_HWC, 0, dataformats='HWC')
writer.close()
P9:Transforms的使用
Transforms.py源码可见Ubuntu Pastebin
在里面,我们看class ToTensor
class ToTensor:
"""Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor. This transform does not support torchscript.
Converts a PIL Image or numpy.ndarray (H x W x C) in the range
[0, 255] to a torch.FloatTensor of shape (C x H x W) in the range [0.0, 1.0]
if the PIL Image belongs to one of the modes (L, LA, P, I, F, RGB, YCbCr, RGBA, CMYK, 1)
or if the numpy.ndarray has dtype = np.uint8
In the other cases, tensors are returned without scaling.
.. note::
Because the input image is scaled to [0.0, 1.0], this transformation should not be used when
transforming target image masks. See the `references`_ for implementing the transforms for image masks.
.. _references: https://github.com/pytorch/vision/tree/master/references/segmentation
"""
def __call__(self, pic):
"""
Args:
pic (PIL Image or numpy.ndarray): Image to be converted to tensor.
Returns:
Tensor: Converted image.
"""
return F.to_tensor(pic)
def __repr__(self):
return self.__class__.__name__ + '()'
它可以把PIL.Image或numpy.ndarray类型转换为tensor类型
而这是通过它的实例化对象的__call__函数实现的
看以下源码
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
img_path = "data/train/ants_image/0013035.jpg"
img = Image.open(img_path)
writer = SummaryWriter("logs")
print(img)
tensor_trans = transforms.ToTensor()
print(type(tensor_trans))
tensor_img = tensor_trans(img)
print(type(tensor_img))
writer.add_image("tensor_img", tensor_img, 0)
writer.close()
终端输出是
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=768x512 at 0x2D304BC20A0>
<class 'torchvision.transforms.transforms.ToTensor'>
<class 'torch.Tensor'>
可见ToTensor是一个类 <class ‘torchvision.transforms.transforms.ToTensor’>
而tensorboard则如图5

本集get:
(1)__call__(),可以把实例化对象像函数一样使用,eg: 对象名()
(2)__rept__(),用于type(对象名)的时候返回。
(3)pycharm快捷键: ctrl + shift + F10 运行现在编辑的py脚本。
(4)pycharm快捷键: alt + 7 显示源码结构如图6。

P10:常见的transforms
总源码 Ubuntu Pastebin
Normalize归一化处理
#Normalize
trans_nor = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
trans_nor_2 = transforms.Normalize([1,2,3],[3,2,1])
#result = (input - 0.5)/0.5 input[0, 1] result[-1, -1]
# Tensor -> Tensor
tensor_img_nor = trans_nor(tensor_img)
tensor_img_nor_2 = trans_nor_2(tensor_img)
writer.add_image("Normalize", tensor_img_nor, 1)
writer.add_image("Normalize", tensor_img_nor_2, 2)
Normalize构造函数传进去的两个矩阵中的3个元素对应的是对Tensor中3层矩阵的处理参数
#result = (input – 0.5)/0.5 input[0, 1] result[-1, -1]
# Tensor -> Tensor
效果如下



Resize改变图片size

#Resize
print(img.size)
trans_resize = transforms.Resize((128, 256)) #默认传参为一个size
trans_resize_test = transforms.Resize(128) #若使用一个参数,行数变为参数,列数等比例缩放
# PIL.Image or torch.Tensor -> PIL.Image or torch.Tensor
img_resize = trans_resize(img)
img_resize_test = trans_resize_test(img)
print(type(img_resize))
img_resize = tensor_trans(img_resize)
img_resize_test = tensor_trans(img_resize_test)
print(type(img_resize))
#writer.add_image("Resize", img, 0, dataformats='HWC')
writer.add_image("Resize", img_resize, 0)
writer.add_image("Resize", img_resize_test, 1)
print(img_resize.size)
Resize可以PIL.Image or torch.Tensor -> PIL.Image or torch.Tensor
传参进去若是一个数值的话, 行数变为参数,列数等比例缩放,证明如下

我们可以查看这张图片的原始尺寸

而用单参数的Resize变换后


我们通过计算 width/height,可以发现,原始图片和变换图片的宽高比都约等于1.3,可见推断成立
Compose转换类组合
#Compose
trans_compose = transforms.Compose([trans_resize, tensor_trans])
img_com = trans_compose(img)
writer.add_image("Compose", img_com, 0)
Compose可以将transforms里面的类进行一个组合,用于对某对象进行一些列的处理
RandomCrop随机裁剪
#RandomCrop
trans_ran = transforms.RandomCrop(256)
trans_compose_2 = transforms.Compose([trans_ran, tensor_trans])
img_path = "desktop.png"
img = Image.open(img_path)
for i in range(10):
img_ran = trans_compose_2(img)
writer.add_image("RandomCrop", img_ran, i)
传参为size,和Resize类似,return为图片的随机一个裁切部分
PIL.Image or torch.Tensor -> PIL.Image or torch.Tensor

