Pytorch学习记录[1]

本文内容包括: Dataset类、Tensorboard的使用、几种常见的Transforms

项目全源码及数据集:http://netdisk.scutvk.cn/pytorch_lesson_1.zip

P7:Dataset类代码实战

此处用到的数据集结构如图1

图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 文档

os.listdir(path=’.’)

返回一个包含由 path 指定目录中条目名称组成的列表。 该列表按任意顺序排列,并且不包括特殊条目 '.' 和 '..',即使它们存在于目录中。 如果有文件在调用此函数期间在被移除或添加到目录中,是否要包括该文件的名称并没有规定。

path 可以是 类路径对象。如果 path 是(直接传入或通过 PathLike 接口间接传入) bytes 类型,则返回的文件名也将是 bytes 类型,其他情况下是 str 类型。

本函数也支持 指定文件描述符为参数,其中描述符必须指向目录。

引发一个 审计事件 os.listdir,附带参数 path

(4):os.path.join() #返回两个路径合并 os.path — 常用路径操作 — Python 3.8.12 文档

os.path.join(path*paths)

智能地拼接一个或多个路径部分。 返回值是 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里面的数据重新画图。

图2、相同tag画图效果
图3、项目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 暂时看不懂是干嘛的

图4
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

图5

本集get:

(1)__call__(),可以把实例化对象像函数一样使用,eg: 对象名()

(2)__rept__(),用于type(对象名)的时候返回。

(3)pycharm快捷键: ctrl + shift + F10 运行现在编辑的py脚本。

(4)pycharm快捷键: alt + 7 显示源码结构如图6。

图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

效果如下

图7、原Tensor图
图8、 trans_nor 处理
图9、 trans_nor_2 处理

Resize改变图片size

图10、Resize((128,256))效果
#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

传参进去若是一个数值的话, 行数变为参数,列数等比例缩放,证明如下

图11、原始图片

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

图12

而用单参数的Resize变换后

图13、单参数Resize变换后
图14、变换后的size

我们通过计算 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

图15、原传入img
图16、随机裁切的效果

学习视频:PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】_哔哩哔哩_bilibili

发表回复