Pytorch入门上 —— Dataset、Tensorboard、Transforms、Dataloader
本节内容参照小土堆的pytorch入门视频教程。学习时建议多读源码,通过源码中的注释可以快速弄清楚类或函数的作用以及输入输出类型。
Dataset
借用Dataset
可以快速访问深度学习需要的数据,例如我们需要访问如下训练数据:
其中,train
中存放的是训练数据集,ants
和bees
既是文件夹名称也是其包含的图片数据的标签,val
中存放的是验证数据集。
假如我们希望自己的Dataset
类可以实现如下数据访问形式:
dataset = MyDataset("root_dir", "label_dir")
img, label = dataset[0] # 通过下标访问
我们只需要继承Dataset
类并覆盖其中的__getitem__()
(必须)和__len__()
(建议)方法。详情可在交互模式中执行如下语句查看:
from torch.utils.data import Dataset
help(Dataset)
# 或执行 Dataset??
# 或在pycharm中按住ctrl并点击Dataset直接查看源码
部分输出如下:
An abstract class representing a :class:`Dataset`.
All datasets that represent a map from keys to data samples should subclass it. All subclasses should overwrite :meth:`__getitem__`, supporting fetching a data sample for a given key. Subclasses could also optionally overwrite :meth:`__len__`, which is expected to return the size of the dataset by many :class:`~torch.utils.data.Sampler` implementations and the default options of :class:`~torch.utils.data.DataLoader`.
定义MyDataset
类:
import os
from torch.utils.data import Dataset
from torch.utils.data.dataset import T_co
from PIL import Image
class MyDataset(Dataset):
def __init__(self, root_dir, label_dir):
"""根据路径获取到所有的 image 文件名
:param root_dir: data路径
:param label_dir: label
"""
self.root_dir = root_dir
self.label_dir = label_dir
self.data_dir = os.path.join(self.root_dir, self.label_dir)
self.img_names = os.listdir(self.data_dir)
def __getitem__(self, index) -> T_co:
"""overwrite 后才可以通过下标访问 dataset
:param index: 下标
:return: img(PIL), label
"""
img_name = self.img_names[index]
img_path = os.path.join(self.data_dir, img_name)
img = Image.open(img_path)
label = self.label_dir
return img, label # 放回什么数据由自己定义
def __len__(self):
return len(self.img_names)
使用MyDataset
类
在交互模式中导入MyDataset
并执行如下语句:
ants_dataset = MyDataset("dataset/train", "ants") # 创建dataset
bees_dataset = MyDataset("dataset/train", "bees")
img, label = ants_dataset[47] # 通过下标访问数据
len(bees_dataset) # 获取dataset长度
img.show() # 显示图片
dataset = ants_dataset + bees_dataset # 将dataset相加
Tensorboard
使用tensorboard
可以按照日志的形式记录训练模型时产生的一些数据(标量、图片等),同时也可以对记录的数据进行可视化,方便我们对现有模型进行分析。例如,借助tensorboard
记录loss
函数随着训练训练周期的下降过程并可视化。
使用Tensorboard
首先要在conda
环境中通过如下命令进行安装:
conda install tensorboard
记录标量(scalar
)
记录标量需要用到torch.utils.tensorboard
中的SummaryWriter
类的add_scalar
方法。
创建如下python
脚本并执行:
from torch.utils.tensorboard import SummaryWriter
# 参数:log_dir,日志存放的路径名称
writer = SummaryWriter("logs")
# 记录曲线 y = x^2
# 参数一:tag,数据标识符
# 参数二:scalar_value,标量值,y轴数据
# 参数三:global_step,步骤,x轴数据
for i in range(100):
writer.add_scalar("y = x^2", i ** 2, i)
# 调用close() 确保数据刷入磁盘
writer.close()
脚本执行成功后会在脚本文件所在当前路径下创建logs
文件夹并生成日志文件。日志生成成功后在conda
环境中执行以下命令来启动tensorboard
可视化服务:
# 指明log存放的位置以及服务启动时使用的端口,默认端口为6006
# 多人使用同一台服务器时应该避免以下两个参数值相同
tensorboard --logdir=second/logs --port=6008
该命令执行成功后会给出如下图中的提示:
在浏览器中打开以上链接即可查看可视化情况:
注意:Tensorboard
是根据不同的tag
来进行可视化的,如果多次写入的日志有相同tag
,可能会导致可视化时出问题。只需删除日志并重新生成,然后重启可视化服务即可解决。
记录图像(image
)
记录图像可以使用torch.utils.tensorboard
中的SummaryWriter
类的add_image
方法。
该方法定义如下:
def add_image(self, tag, img_tensor, global_step=None, walltime=None, dataformats='CHW')
# tag(string) 同上
# img_tensor (torch.Tensor, numpy.array, or string/blobname): 图像数据
# global_step (int): 步骤
# dataformats (string):图像数据格式,不为CHW则需要指明
创建如下python
脚本并执行:
from torch.utils.tensorboard import SummaryWriter
from PIL import Image
import numpy as np
writer = SummaryWriter("logs")
img_path = "E:\\code\\python\\pytorch-learn\\dataset\\train\\ants\\0013035.jpg"
# img 为 PIL.JpegImagePlugin.JpegImageFile 类型
img = Image.open(img_path)
img_array = np.array(img)
writer.add_image("ants", img_array, 0, dataformats="HWC")
writer.close()
脚本执行成功后按之前同样的方式启动可视化服务即可。
如果日志中同一个tag
标识的数据有多个global_step
则可以如下图所示:拖动轴来查看global_step
之间的图像变化。
Transforms
transforms
是位于torchvision
包中的一个模块,模块中有多个类可以对将要输入神经网络的数据进行转换以适应网络的需要。
ToTensor
该类可以将 PIL Image
or numpy.ndarray
转换为张量,以方便图像输入到神经网络中,如下代码做了简单示例:将PIL Image
转换为张量后借助tensorboard
直接写入日志文件:
from torchvision import transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
# 创建 transforms 模块中的 ToTensor 类
trans_to_tensor = transforms.ToTensor()
# 获取 PIL 图像
img_path = "E:/code/python/pytorch-learn/dataset/train/ants" \
"/6743948_2b8c096dda.jpg "
img = Image.open(img_path)
# 将 PIL 图像转化为 tensor 类型
# 1. 将输入的数据shape W,H,C ——> C,W,H
# 2. 将所有数除以255,将数据归一化到[0,1]
# 实例像方法一样调用,这里实际调用的是ToTensor类中的__call__方法,
# 更多:https://zhuanlan.zhihu.com/p/370234492
img_tensor = trans_to_tensor(img)
# 使用 tensorboard 将 tensor 图像写入日志
writer = SummaryWriter("logs")
writer.add_image("img_tensor", img_tensor, 1)
writer.close()
启动tensorbard
可视化服务并在浏览器中访问:
Normalize
normalize
类可以利用均值和标准差对tensor
图像进行归一化。该类构造函数定义如下:
# mean (sequence): 代表图像每个通道均值的序列
# std (sequence): 代表图像每个通道标准差的序列
# inplace(bool,optional): 是否原地修改tensor
def __init__(self, mean, std, inplace=False):
该类的具体归一化操作如下:
output[channel] = (input[channel] - mean[channel]) / std[channel]
拿任意通道来说,如果原值 value
为 [0, 1]
,传入的 mean
为 0.5
,std
为 0.5
那么归一化操作即为:(value - 0.5) / 0.5 = 2*value - 1
,带入value
范围后计算得到value
的新范围为[-1, 1]
。
创建如下脚本并执行:
from torchvision import transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
# 创建 transforms 模块中的 ToTensor 类
trans_to_tensor = transforms.ToTensor()
# 创建 transforms 模块中的 Normalize 类,三个通道的mean和std都为0.5
trans_normalize = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
# 获取 PIL 图像
img_path = "E:/code/python/pytorch-learn/dataset/train/ants" \
"/6743948_2b8c096dda.jpg "
img = Image.open(img_path)
# 将 PIL 图像转化为 tensor 类型
# 1. 将输入的数据shape W,H,C ——> C,W,H
# 2. 将所有数除以255,将数据归一化到[0,1]
img_tensor = trans_to_tensor(img)
# 将 tensor 进行归一化
img_normalize = trans_normalize(img_tensor)
print(type(img_normalize))
# <class 'torch.tensor'="">
# 使用 tensorboard 将 tensor 图像写入日志
writer = SummaryWriter("logs")
writer.add_image("img_normalize", img_normalize, 1)
writer.close()
启动tensorbard
可视化服务并在浏览器中访问:
Resize
Resize
类可对tensor
或PIL
图像进行缩放,构造函数定义如下:
# size (sequence or int): 所需的输出大小
# 如果size是形如(h, w)的高和宽的序列,输出时就按此匹配;
# 如果size是一个int,则选择小的边来进行等比例缩放,如果 height > width,
# 则缩放后为(size * height / width, size)
def __init__(self, size, interpolation=InterpolationMode.BILINEAR, max_size=None, antialias=None):
创建如下脚本并执行:
from torchvision import transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
# 创建到 transforms 模块中的 ToTensor 类
trans_to_tensor = transforms.ToTensor()
# 创建 transforms 模块中的 Normalize 类
trans_normalize = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
# 创建 transforms 模块中的 Resize 类
trans_resize_52x52 = transforms.Resize((80, 40))
trans_resize_52 = transforms.Resize(200)
# 获取 PIL 图像
img_path = "E:/code/python/pytorch-learn/dataset/train/ants" \
"/6743948_2b8c096dda.jpg "
img = Image.open(img_path)
# 使用 tensorboard 记录多个处理步骤的图像
writer = SummaryWriter("logs")
# 将 PIL 图像转化为 tensor 类型
# 1. 将输入的数据shape W,H,C ——> C,W,H
# 2. 将所有数除以255,将数据归一化到[0,1]
img_tensor = trans_to_tensor(img)
# 步骤0:记录tensor图像
writer.add_image("img-tensor-normalize-resize_52x52_resize_52", img_tensor, 0)
# 将 tensor 进行归一化
img_normalize = trans_normalize(img_tensor)
# 步骤1:记录normalize后的图像
writer.add_image("img-tensor-normalize-resize_52x52_resize_52",
img_normalize, 1)
# 对normalize后的tensor进行(52, 52)的resize
img_resize_52x52 = trans_resize_52x52(img_normalize)
# 步骤2:记录(52, 52)resize后的图像
writer.add_image("img-tensor-normalize-resize_52x52_resize_52",
img_resize_52x52, 2)
# 对(52, 52)resize后的tensor再次进行(52)的resize
img_resize_52 = trans_resize_52(img_resize_52x52)
# 步骤3:记录(52)resize后的图像
writer.add_image("img-tensor-normalize-resize_52x52_resize_52",
img_resize_52, 3)
writer.close()
启动tensorbard
可视化服务并在浏览器中访问:
Compose
在上一节的示例中,我们对图像的处理是基于一系列transform
,为了记录每个transform
执行后的图像,我们在每两个transform
之间插入了记录图像操作。如果我们不需要记录每个transform
执行后的图像,那么我们可以通过Compose
来顺序执行一系列transform
。
Compose
类的构造函数定义如下:
# transforms (list of Transform objects):
# 需要顺序执行的transfrom对象组成的list
def __init__(self, transforms):
该类的核心思想位于__call__
函数中:
def __call__(self, img):
for t in self.transforms:
img = t(img)
return img
有了compose
后,为了得到上一节最后的结果,我们可以对上一节的脚本做如下简化:
from torchvision import transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
# 创建到 transforms 模块中的 ToTensor 类
trans_to_tensor = transforms.ToTensor()
# 创建 transforms 模块中的 Normalize 类
trans_normalize = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
# 创建 transforms 模块中的 Resize 类
trans_resize_80x40 = transforms.Resize((80, 40))
trans_resize_200 = transforms.Resize(200)
# 创建 transforms 模块中的 Compose 类
trans_compose = transforms.Compose([trans_to_tensor, trans_normalize,
trans_resize_80x40, trans_resize_200])
# 获取 PIL 图像
img_path = "E:/code/python/pytorch-learn/dataset/train/ants" \
"/6743948_2b8c096dda.jpg "
img = Image.open(img_path)
# 执行 Compose
img_compose = trans_compose(img)
# 记录compose后的图像
writer = SummaryWriter("logs")
writer.add_image("img-tensor-normalize-resize_80x40-resize_200-compose",
img_compose, 0)
writer.close()
启动tensorbard
可视化服务并在浏览器中访问:
transforms
模块中还有很多其他的transform
类,这些类在需要时查询文档即可。查询文档时,重点要弄清楚类或函数的作用以及输入输出类型。
Dataset
和 Transforms
结合使用
在pytorch
官网首页,按不同模块分别提供了文档,包括:核心模块,音频模块,文本模块,视觉模块等:
在torchvision.datasets
中,提供了对常用数据集的支持,它可以帮助我们下载、解压并快速使用数据集:
以CIFAR10
数据集为例,根据如下文档使用即可:
根据文档可以知道,要使用CIFAR10
数据集则使用torchvision.datasets.CIFAR10
类,该类的构造函数要求传入如下参数:
# root (string):数据集所在目录
# train (bool, optional):为True,创建训练集;为false,创建测试集
# transform (callable, optional):处理 PIL image 的function/transform
# target_transform (callable, optional):处理 target(图像类别)的function/transform
# download (bool, optional):为true则下载数据集到root目录中,如果已经存在则不会下载
def __init__(
self,
root: str,
train: bool = True,
transform: Optional[Callable] = None,
target_transform: Optional[Callable] = None,
download: bool = False,
) -> None:
根据文档中的__getitem__
函数我们知道,通过下标访问Dataset
时会返回(image, target)
,即图像和图像的种类。
创建如下脚本并执行(使用训练集):
import torchvision
# 创建transform,将 PIL Image 转换为 tensor
data_trans = torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
# 建议download始终为True,即使你自己提前下载好了数据集
# 下载慢的话可以拷贝控制台输出的下载地址,然后到迅雷下载好后再将压缩包拷贝至root下即可
# 下载地址也可以在torchvision.datasets.CIFAR10类的源码中查看
train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True,
transform=data_trans, download=True)
img, target = train_set[0]
print(type(img)) # <class 'torch.tensor'="">
print(target) # 6
# ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
print(train_set.classes)
print(train_set.classes[target]) # frog
创建如下脚本并执行(使用测试集):
import torchvision
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False,
download=True)
img, target = test_set[0]
img.show()
print(type(img)) # <class 'pil.image.image'="">
print(target) # 3
print(test_set.classes[target]) # cat
DataLoader
借用DataLoader
可以将数据输入神经网络,查看官方文档:
根据官方文档我们知道,在创建DataLoader
时需要的主要的参数如下:
# dataset (Dataset): Dataset类型数据集
# batch_size (int, optional): 批大小,default: 1
# shuffle (bool, optional): 每个 epoch 后是否打乱数据,default: False
# num_workers (int, optional): 加载数据时使用的子进程数量,为0表示使用主进程加载,default: 0。可设置为你的cpu核心数
# drop_last (bool, optional): 当数据个数不能被batch_size整除时,为True表示丢弃最后一批,为False表示不丢弃,default: False
def __init__(self, dataset: Dataset[T_co], batch_size: Optional[int] = 1,
shuffle: bool = False, sampler: Optional[Sampler] = None,
batch_sampler: Optional[Sampler[Sequence]] = None,
num_workers: int = 0, collate_fn: Optional[_collate_fn_t] = None,
pin_memory: bool = False, drop_last: bool = False,
timeout: float = 0, worker_init_fn: Optional[_worker_init_fn_t] = None,
multiprocessing_context=None, generator=None,
*, prefetch_factor: int = 2,
persistent_workers: bool = False):
创建如下脚本并执行:
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
def train():
trans_to_tensor = torchvision.transforms.ToTensor()
train_dataset = torchvision.datasets.CIFAR10(root="./dataset",
transform=trans_to_tensor,
download=True)
# dataloader 按照batch_size打包img和target,如图所示
train_dataloader = DataLoader(dataset=train_dataset, batch_size=64,
shuffle=False, num_workers=16,
drop_last=False)
writer = SummaryWriter("logs")
for epoch in range(2):
for batch_num, batch_data in enumerate(train_dataloader):
# print(epoch, " : ", batch_num)
batch_img, batch_target = batch_data
writer.add_images(
"CIFAR10-batch64-no_shuffle-no_drop_last_epoch{}".format(epoch)
, batch_img, batch_num)
writer.close()
if __name__ == "__main__":
train()
在windows
中如果num_workers > 1
报错,解决方法如下:
1.修改代码为如下格式:
def train():
# Here was inserted the whole code that train the network ...
if __name__ == '__main__':
train()
2.如果代码修改后仍然报错:OSError: [WinError 1455] 页面文件太小,无法完成操作
。则还需要调整你的python
环境所在盘的虚拟内存大小:
进入高级系统设置
:
点击:高级 -> 性能 -> 设置
:
点击:高级 -> 更改
:
按照下图,将D
盘的虚拟内存调整为系统管理的大小
(重启后生效)。任然报错就自定义大小为一个很大的值,如:10GB-100GB
。
脚本执行成功后,启动tensorbard
可视化服务并在浏览器中访问:
step=170
时:(日志比较大,浏览器中可能需要多次刷新才能完全加载)
step=781
时:
由于我们在创建DataLoader
时,参数shuffle=False
使得epoch0
和epoch1
之间相同批次抽取的图像是相同的;参数drop_last=False
使得即使最后一个批次图像不足64也没有被舍弃。
修改脚本使得:shuffle=True
,drop_last=True
,然后再次执行脚本并启动tensorbard
可视化服务。浏览器中访问结果如下:
如图所示,epoch0
和epoch1
之间相同批次(780)抽取的图像不相同,且第781个批次被舍弃。
Pytorch入门上 —— Dataset、Tensorboard、Transforms、Dataloader的更多相关文章
- Pytorch入门中 —— 搭建网络模型
本节内容参照小土堆的pytorch入门视频教程,主要通过查询文档的方式讲解如何搭建卷积神经网络.学习时要学会查询文档,这样会比直接搜索良莠不齐的博客更快.更可靠.讲解的内容主要是pytorch核心包中 ...
- Pytorch入门下 —— 其他
本节内容参照小土堆的pytorch入门视频教程. 现有模型使用和修改 pytorch框架提供了很多现有模型,其中torchvision.models包中有很多关于视觉(图像)领域的模型,如下图: 下面 ...
- Pytorch入门随手记
Pytorch入门随手记 什么是Pytorch? Pytorch是Torch到Python上的移植(Torch原本是用Lua语言编写的) 是一个动态的过程,数据和图是一起建立的. tensor.dot ...
- Pytorch入门——手把手教你MNIST手写数字识别
MNIST手写数字识别教程 要开始带组内的小朋友了,特意出一个Pytorch教程来指导一下 [!] 这里是实战教程,默认读者已经学会了部分深度学习原理,若有不懂的地方可以先停下来查查资料 目录 MNI ...
- pytorch 入门指南
两类深度学习框架的优缺点 动态图(PyTorch) 计算图的进行与代码的运行时同时进行的. 静态图(Tensorflow <2.0) 自建命名体系 自建时序控制 难以介入 使用深度学习框架的优点 ...
- pytorch入门2.2构建回归模型初体验(开始训练)
pytorch入门2.x构建回归模型系列: pytorch入门2.0构建回归模型初体验(数据生成) pytorch入门2.1构建回归模型初体验(模型构建) pytorch入门2.2构建回归模型初体验( ...
- pytorch入门2.1构建回归模型初体验(模型构建)
pytorch入门2.x构建回归模型系列: pytorch入门2.0构建回归模型初体验(数据生成) pytorch入门2.1构建回归模型初体验(模型构建) pytorch入门2.2构建回归模型初体验( ...
- pytorch入门与实践-2.2
Tensor 1--本质上可以理解为具有不同维度的数组 2--支持的基本运算 |---创建Tensor: x=t.tensor(x,y) x,y 表示数组的大小 , x=t.rand(x,y), x ...
- [pytorch] Pytorch入门
Pytorch入门 简单容易上手,感觉比keras好理解多了,和mxnet很像(似乎mxnet有点借鉴pytorch),记一记. 直接从例子开始学,基础知识咱已经看了很多论文了... import t ...
随机推荐
- [noi110]翘课
发现加边操作不好处理,因此考虑先加完所有边后删边. 删去一对边x到y,如果两者中有一个不翘课显然没有意义,那么如果都翘课了那么就对他们进行判断,如果无法翘课就继续搜下去. 这样的时间复杂度看上去似乎是 ...
- shiro 学习笔记
1. 权限管理 1.1 什么是权限管理? 权限管理实现对用户访问系统的控制,按照安全规则或者安全策略,可以控制用户只能访问自己被授权的资源 权限管理包括用户身份认证和授权两部分,简称认证授权 1.2 ...
- Hi3516开发笔记(五):通过HiTools使用网口将uboot、kernel、roofts和userdata按照分区表烧写镜像
前言 前面生成了uboot,kernel,sample,userdata(我们实际修改了ip的),rootfs,现在需要烧写进入核心板. 使用网口烧写镜像(海思烧写必须占用调试串口) 步骤一: ...
- .NET E F(Entity Framework)框架 DataBase First 和 Code First 简单用法。
EF是微软.NET平台官方的ORM(objet-relation mapping),就是一种对象-关系 映射,是将关系数据库种的业务数据用对象的形式表现出来,并通过面向对象的方式讲这些对象组织起来,实 ...
- C/C++转义字符表
转义字符 意义 ASCII码值(十进制) \a 响铃(BEL) 007 \b 退格(BS) ,将当前位置移到前一列 008 \f 换页(FF),将当前位置移到下页开头 012 \n 换行(LF) ,将 ...
- java Scanner类与String类
Scanner类的使用: 导入Scanner包,new Scanner对象,记得调用System.in参数,调用对应next方法实现键盘录入. import java.util.Arrays; i ...
- linux中conda升级R到4.0?
目录 前言 问题 曲线救国 前言 虽然我的win版本R已经用4了,但之前在Linux环境一直没用R4.0,因为Linux涉及的东西太多,担心不稳定,牵一发而动全身. 但现在有好些R包必须要用更新到R4 ...
- Anaconda3-更换为清华源后依旧报错CondaHTTPError: HTTP 000 CONNECTION FAILED
前言 今天发现换完清华源以后依旧报错 CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://mirrors.tuna.tsi.. ...
- 【6】蛋白质组学鉴定定量软件之MaxQuant
目录 1.简介 2.下载安装 3.配置与运行 4.结果 5.Perseus后处理 6.小结 1.简介 2016年,德国马普所的Cox和蛋白质组学领域巨擘Matthias Mann合作开发了MaxQua ...
- OOM机制
Linux内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内 ...