初识Pytorch(一) -- Transforms笔记

测试torch是否安装成功

1
2
3
import torch
torch.cuda.is_available()
# True # 若安装成功,则会打印true

蚂蚁蜜蜂分类数据集和下载连接:https://download.pytorch.org/tutorial/hymenoptera_data.zip

1 函数功能查看

1
2
dir(torch.cuda)	打开,看见
help(函数名不带括号) 说明书

python中__call__的用法

  • 在类中双下划线表示是类的内置函数
  • __call__的情况下,调用对象时,可以直接传参到call中
  • 没有__call__的情况下,调用对象需要加上“.”来调用其中的方法

**python中__getitem__的用法**

  • 在进行索引取值时自动调用,可以查看原对象中__getitem__return值

不知道返回值时

  • print()
  • print(type())
  • 打断点 debug

2 torch数据加载(读取)

pytorch官网文档地址:https://docs.pytorch.org

1
2
3
from torch.utils.data import Dataset
from PIL import Image
import os

2.1 查看Dataset说明

说明文档查看:

1
2
3
4
5
6
7
# help(Dataset)
Dataset??
# 双?打印的结果更易读
# dataset # 提供一种方式去获取数据及其label
# 如何获取每一个数据及其label
# 告诉我们总共有多少的数据
# dataloader # 为后面的网络提供不同的数据形式

2.2 初始化类操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyData(Dataset):
def __init__(self, root_dir, label_dir):
# 初始化,为整个class提供一个全局变量
self.root_dir = root_dir # 数据根目录
self.label_dir = label_dir # 数据标签目录
self.path = os.path.join(self.root_dir, self.label_dir) # 拼接目录
self.img_path = os.listdir(self.path) # 读取目录中所有文件名

def __getitem__(self, index):
# index作为一个编号
# 首先获取图片地址的列表,通过index获取图片的存储地址
img_name = self.img_path[index] # 单张图片文件名
img_item_path = os.path.join(self.path, img_name) # 拼接文件路径
img = Image.open(img_item_path) # 打开图片,用变量img加载
label = self.label_dir # 图片标签
return img, label

def __len__(self):
return len(self.img_path) # 返回整个数据集有多少数据

2.3 查看数据

1
2
3
4
5
6
7
8
9
root_dir = './dataset/train'
ants_label_dir = 'ants'
bees_label_dir = 'bees'
ants_dataset = MyData(root_dir, ants_label_dir) # 蚂蚁数据集
bees_dataset = MyData(root_dir, bees_label_dir) # 蜜蜂数据集

# 单个数据查看
img, label = ants_dataset[10]
img, label = bees_dataset[10]

3 图片数据读取

将图片数据读取为其他格式:(torch.Tensor, numpy.ndarray, string/blobname)

3.1 PIL读取图片

PIL读取图片后,得到的数据类型为PIL.Image.Image

1
2
3
from PIL import Image
img = Image.open(img_path) # img_path为图片路径
print(type(img))

3.2 Numpy转换PIL格式

利用numpy.array(),对PIL图片进行转换

转换后的图片类型为numpy.ndarray

1
2
3
import numpy as np
img_array = np.array(img)
print(type(img_array))

3.3 Opencv打开图片

利用Opencv读取图片,获取numpy型图片数据

安装Opencv:pip install opencv-python

1
2
3
import cv2

cv_img = cv2.imread(img_path) # cv_img是ndarray类型

4 TensorBoard 使用

TensorBoard的安装:pip install tensorboard

TensorBoard用来显示模型训练到xx步时,模型的output是什么样,如损失函数、训练结果

启动命令:tensorboard --logdir=logs --port=6007

logdir相对路径logs,port手动设置为6007

4.1 .add_scalar() 用法

1
2
3
4
5
6
7
8
9
10
11
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter('logs') # 存储路径为当前目录下创建logs文件夹

# .add_scalar()用法示例
for i in range(100):
writer.add_scalar(
tag='Y=2x', # 图表的标题
scalar_value=2*i, # 图表的纵轴,需要保存的数值
global_step=i, # 图表的横轴,训练的步数
)

在终端输入:tensorboard --logdir=logs --port=6007

打开网页查看训练数据

4.2 .add_image() 用法

PILnumpy,需要在add_image()中指定shape中每一个数字/维表示的含义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image

writer = SummaryWriter('logs')
image_path = 'dataset/train/ants/0013035.jpg'
img_PIL = Image.open(image_path)
img_array = np.array(img_PIL) # numpy.ndarray格式图片数据
print(img_array.shape) # 查看图像格式,通道数3在最后一位

# .add_image()用法示例
writer.add_image(
tag='test', # 图像标题
img_tensor=img_array, # 图像数据如 (torch.Tensor, numpy.ndarray, or string/blobname)
global_step=1, # 训练步数
dataformats='HWC' # 图像格式(高、宽、通道数)
)

5 Transforms 使用

transform基本上是对图片进行变化,使用方式是把一些特定格式的图片,通过Transform工具输出为我们想要的结果。

具体使用方式直接查看transforms.py文件中相关方法的使用说明。

  1. 使用某方法时,首先关注它的输入和输出类型
  2. 多看transforms.py文件内容
  3. 关注方法需要什么参数

5.1 Transform 的结构和用法

使用pycharm左侧工具栏的结构功能查看transforms.py的结构

可以把transforms.py看成一个工具箱,里面有各种工具如totensor(把一些数据类型转化为tensor类型)、resize、、、

1
2
3
from torchvision import transforms
from PIL import Image
import cv2

首先要搞懂tensor数据类型,通过Transforms.ToTensor去解决两个问题

  1. transforms该如何使用(python)
  2. 为什么需要Tensor数据类型
    • a.tensor数据类型是包装了神经网络理论基础的参数
1
2
3
4
5
6
7
img_path = 'dataset/train/ants/6240338_93729615ec.jpg'
# img = Image.open(img_path) # 使用PIL读取图片
img = cv2.imread(img_path) # 使用opencv读取图片

# 转为tensor格式
tensor_trans = transforms.ToTensor()
tensor_img = tensor_trans(img)

tensor_img是一个tensor的数据类型,它的参数:

  • _backward_hooks:神经网络中的反向传播,根据结果对参数进行调整
  • _grad:梯度
  • _grad_fn:梯度的方法
  • data:图片的具体数据

5.2 使用tensorboard记录

1
2
3
4
5
6
7
8
9
10
11
12
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter(
log_dir='logs', # 日志存放的文件夹
)

writer.add_image(
tag='Tensor_img', # 日志显示的图片名称
img_tensor=tensor_img, # (torch.Tensor, numpy.ndarray, or string/blobname)
)

writer.close()

5.3 常见的Transforms

需关注transforms.py中各函数的

  1. 输入——PIL ——Image.open()
  2. 输出——tensor ——ToTensor()
  3. 作用——narrays ——cv.imread()

5.3.1 Compose类

把不同的transform结合在一起,比如有张图片要处理,经过compose类时,首先进行一个中心的裁剪,再转为tensor数据类型…

Compose()中的参数是一个列表,列表内容是transforms类型

1
2
3
4
5
6
Example:
>>> transforms.Compose([
>>> transforms.CenterCrop(10),
>>> transforms.PILToTensor(),
>>> transforms.ConvertImageDtype(torch.float),
>>> ])

5.3.2 ToTensor类

输入必须时PIL Imagendarray 三通道图片

输出为Tensor数据类型

1
2
3
4
5
6
7
8
9
10
11
12
from PIL import Image
from torchvision import transforms
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter('logs')
img = Image.open('images/IMG_202408121501_3840x2161.png')

# ToTensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img) # 转为tensor格式
writer.add_image('ToTensor', img_tensor) # 调用tensorboard保存日志
writer.close()

5.3.3 Normalize类

归一化一个 tensor 类型的 image,根据它的均值标准差

数据 = (输入 - 均值) / 标准差

1
2
3
4
5
6
7
8
9
# Normalize

print(img_tensor[0][0][0])
trans_norm = transforms.Normalize(mean=[1, 3, 5], std=[3, 2, 1])
# mean是均值,std是标准差

img_norm = trans_norm(img_tensor)
print(img_norm[0][0][0])
writer.add_image('Normalize', img_norm,2)

5.3.4 Resize类

给定尺寸进行缩放,如果只给定了一个数,Resize就会根据最小的边去等比缩放

1
2
3
4
5
6
7
8
9
10
# Resize
print(img.size)
trans_resize = transforms.Resize((512, 512))
# img PIL -> resize -> img_resize PIL
img_resize = trans_resize(img)

# img_resize PIL -> totensor -> img_resize tensor
img_resize = trans_totensor(img_resize) # PIL转为tensor格式
# print(img_resize)
writer.add_image('Resize', img_resize, 1)

5.3.5 Compose - Resize

应用Compose 将Resize步骤整合,并使用tensorboard记录日志

1
2
3
4
5
6
7
8
9
# Compose - Resize
trans_resize_2 = transforms.Resize(512)

# 将 trans_resize_2 与 trans_totensor 合并
# PIL -> PIL -> tensor
trans_compose = transforms.Compose([trans_resize_2, trans_totensor])

img_resize_2 = trans_compose(img)
writer.add_image('Resize', img_resize_2, 2)

5.3.6 RandomCrop类

随机裁剪,输入一个序列如(h, w)或一个整数值size,会按输入裁剪为一个(h,w)或一个正方形(size, size)

1
2
3
4
5
6
7
# RandomCrop
# trans_random = transforms.RandomCrop(512)
trans_random = transforms.RandomCrop((512, 1024))
trans_compose_2 = transforms.Compose([trans_random, trans_totensor])
for i in range(10):
img_crop = trans_compose_2(img)
writer.add_image('RandomCropHW', img_crop, i)

5.4 结合 Datasets 使用

pytorch官方提供的数据集:https://docs.pytorch.org/vision/stable/datasets.html

CIFAR-10 dataset,需要设置参数:

1
2
3
4
5
6
7
torchvision.datasets.CIFAR10(
root: Union[str, Path], # 数据集的位置
train: bool = True, # 为True是一个训练集,为False则是一个测试集
transform: Optional[Callable] = None, # 需要对数据集进行什么transform变化
target_transform: Optional[Callable] = None, # 对target进行一个transform变化
download: bool = False # 设置为True时自动从网上下载数据集,为False时不会下载
)

Parameters:

  • root (str or pathlib.Path) – Root directory of dataset where directory cifar-10-batches-py exists or will be saved to if download is set to True.
  • train (bool, optional) – If True, creates dataset from training set, otherwise creates from test set.
  • transform (callable, optional) – A function/transform that takes in a PIL image and returns a transformed version. E.g, transforms.RandomCrop
  • target_transform (callable, optional) – A function/transform that takes in the target and transforms it.
  • download (bool, optional) – If true, downloads the dataset from the internet and puts it in root directory. If dataset is already downloaded, it is not downloaded again.

示例代码

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
import torchvision
from torch.utils.tensorboard import SummaryWriter

# 设置用到的transform方法,通过compose进行组合
dataset_transform = torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])

# 训练集和测试集设置
train_set = torchvision.datasets.CIFAR10(
root='dataset',
train=True,
transform=dataset_transform,
download=True,
)
test_set = torchvision.datasets.CIFAR10(
root='dataset',
train=False,
transform=dataset_transform,
download=True,
)

# 写入tensorboard
writer = SummaryWriter(log_dir='logs')
for i in range(10):
img, target = test_set[i] # 此时的img是tensor数据类型
writer.add_image('test_set', img, i)
writer.close()

6 Dataloader

dataloader是一个数据加载器,把数据加载到神经网络中,通过参数设置如何去dataset中取数据。

官网说明:https://docs.pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader

1
2
3
4
5
6
7
8
9
torch.utils.data.DataLoader(
dataset, batch_size=1, shuffle=None, sampler=None,
batch_sampler=None, num_workers=0, collate_fn=None,
pin_memory=False, drop_last=False, timeout=0,
worker_init_fn=None, multiprocessing_context=None,
generator=None, *, prefetch_factor=None,
persistent_workers=False, pin_memory_device='',
in_order=True
)
  • dataset (Dataset) :自定义的dataset,告诉我们数据集在什么位置,以及具体数据的索引等等。
  • batch_size (int, optional) :每次读取数据的量
  • shuffle (bool, optional) :是否打乱数据顺序,为True则进行打乱操作,默认为False
  • num_workers (int, optional):加载数据时使用单个进程或多个进程,多个进程更快,默认情况是0。不为0时在Windows环境中可能会报错
  • drop_last (bool, optional) :当数据量总数除不尽batch_size时,为True则最后的数据舍去,默认False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import torchvision
# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10(
root='./dataset',
train=False,
download=True,
transform=torchvision.transforms.ToTensor()
)

from torch.utils.data import DataLoader
# 定义数据加载对象
test_loader = DataLoader(
dataset=test_data,
batch_size=4,
shuffle=True,
num_workers=0,
drop_last=False
)

# 测试数据集中第一张图片及target
img, target = test_data[0]

查看dataloader的数据

1
2
3
4
5
6
7
8
9
for data in test_loader:
imgs, targets = data
print(imgs.shape, targets)

# 第一个数据:4张图片,3通道,32*32像素
# 第二个数据:4张图片对应的编号
# torch.Size([4, 3, 32, 32]) tensor([9, 6, 3, 7])
# torch.Size([4, 3, 32, 32]) tensor([7, 1, 7, 0])
# ...

使用tensorboard记录日志

1
2
3
4
5
6
7
8
9
10
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter('logs')
for epoch in range(2): # epoch是指完整遍历数据集一次(完成总样本数量的计算,随机取样时会有重复)
step = 0 # 迭代次数,使用一个批次(batch)数据进行一次参数更新的过程。
for data in test_loader:
imgs, targets = data
# print(imgs.shape, targets)
writer.add_image('Epoch:{}'.format(epoch), imgs, step, dataformats='NCHW')
step = step + 1