pytorch模型训练特征图可视化
- tensorflow使用tensorboard来可视化,pytorch使用tensorboardX来可视化。需要安装。
使用cuda训练
当单个cuda
x = torch.Tensor([1,2,3]).cuda
当多个cuda,不指定时默认使用cuda0
x = torch.Tensor([1,2,3],device = torch.device("cuda:0")
或者这样写也可以
x = torch.Tensor([1,2,3].to(device = torch.device('cuda:0'))
pytorch的扩展
自定义模型、自定义层、自定义激活函数、自定义损失函数都属于pytorch的扩展。
要实现自定义扩展,有两种方式:
- 方式一:通过继承torch.nn.Module类来实现扩展
- 方式一具有的特点是:
- 特点一:torch.nn.functional是专门为神经网络定义的函数集合
- 特点二:只需要重新实现__init__和forward函数,求导的函数是不需要设置的,会自动按照求导规则求导,在Module类里面是没有backward这个函数的
- 特点三:可以保存参数和状态信息
- 方式二:通过继承torch.nn.Function类来实现扩展
- 方式二具有的三个特点是:
- 特点一:在有些操作通过组合pytorch中已经有的层或者是已经有的方法实现不了的时候,比如需要实现一个新的方法,这个新的方法需要forward和backward一起写,然后自己写对中间变量的操作。
- 特点二:需要重新实现__init__和forward函数,以及backward函数,需要自己定义求导规则。
- 特点三: 不可以保存参数和状态信息
-
总结:当不需要自动求导机制,需要自定义求导规则时候,就要扩展torch.autograde.Function类,否则就是用torch.nn.Module类,后面这种是最常用的。nn.Module是一个包装好的类,具体定义了一个网络层,可以维护状态和存储参数信息,而nn.function仅仅提供一个计算,不会维护状态信息和存储参数,在Module内部,层的功能实际上又是通过nn.functional来实现的。对于一些不需要存储参数和状态信息的层,比如activation函数(比如relu和sigmoid等),比如dropout层、pooling层等都没有训练参数,所以可以使用functional模块,但是仍然可以使用nn.Module类完成,后者仍然更常用。
不管是自定义层还是损失函数,方法都多种,但是通过统一的接口nn.Module是最便于查看的。这也是pytorch的优点:模型、层、损失函数的定义具有统一性。都是通过Module类来完成的。
DataLoader
参考:1
- pytorch 的数据加载到模型的操作顺序是这样的:
第一步:创建一个 Dataset 对象
第二步:创建一个 DataLoader 对象
第三步:循环这个 DataLoader 对象,将img, label加载到模型中进行训练
如下代码所示:
dataset = MyDataset()
dataloader = DataLoader(dataset)
num_epoches = 100
for epoch in range(num_epoches):
for img, label in dataloader:
......
-
所以,作为直接对数据进入模型中的关键一步, DataLoader非常重要。
-
DataLoader,它是PyTorch中数据读取的一个重要接口,该接口定义在dataloader.py中
-
只要是用PyTorch来训练模型基本都会用到DataLoader接口,该接口的目的:将自定义的Dataset根据batch size大小、是否shuffle等封装成一个Batch Size大小的Tensor,用于后面的训练。
-
官方对DataLoader的说明是:“数据加载由数据集和采样器组成,基于python的单、多进程的iterators来处理数据。”关于iterator和iterable的区别和概念请自行查阅,在实现中的差别就是iterators有__iter__和__next__方法,而iterable只有__iter__方法。
-
DataLoader本质上就是一个iterable(跟python的内置类型list等一样),并利用多进程来加速batch data的处理,使用yield来使用有限的内存
-
dataloader本质是一个可迭代对象,使用iter()访问,不能使用next()访问;
-
使用iter(dataloader)返回的是一个迭代器,然后可以使用next访问;
-
也可以使用
for inputs, labels in dataloaders
进行可迭代对象的访问; -
实现一个datasets对象,传入到dataloader中;然后内部使用yeild返回每一次batch的数据;
dataset(Dataset): 传入的数据集
batch_size(int, optional): 每个batch有多少个样本
shuffle(bool, optional): 在每个epoch开始的时候,对数据进行重新排序
sampler(Sampler, optional): 自定义从数据集中取样本的策略,如果指定这个参数,那么shuffle必须为False
batch_sampler(Sampler, optional): 与sampler类似,但是一次只返回一个batch的indices(索引),需要注意的是,一旦指定了这个参数,那么 batch_size,shuffle,sampler,drop_last就不能再制定了(互斥——Mutually exclusive)
num_workers (int, optional): 这个参数决定了有几个进程来处理data loading。0意味着所有的数据都会被load进主进程。(默认为0)
collate_fn (callable, optional): 将一个list的sample组成一个mini-batch的函数
pin_memory (bool, optional): 如果设置为True,那么data loader将会在返回它们之前,将tensors拷贝到CUDA中的固定内存(CUDA pinned memory)中.
drop_last (bool, optional): 如果设置为True:这个是对最后的未完成的batch来说的,比如你的batch_size设置为64,而一个epoch只有100个样本,那么训练的时候后面的36个就被扔掉了…
如果为False(默认),那么会继续正常执行,只是最后的batch_size会小一点。
timeout(numeric, optional): 如果是正数,表明等待从worker进程中收集一个batch等待的时间,若超出设定的时间还没有收集到,那就不收集这个内容了。这个numeric应总是大于等于0。默认为0
worker_init_fn (callable, optional): 每个worker初始化函数 If not None, this will be called on each worker subprocess with the worker id (an int in [0,num_workers - 1]) as input, after seeding and before data loading. (default: None)
unsqueeze()函数
在实践中的使用如下:
bottle=torch.Tensor(bottle).permute(2,0,1).unsqueeze(0).div(255).cuda()
下面用例子解释这个函数:
a = t.arange(0,6)
a.view(2,3)
上面的代码会输出:
tensor([[0,1,2],
[3,4,5]])
会有这样的输出是因为a的值从0到6,包括0,不包括6,可以看出a的维度是(2,3)
为了实现在第二维增加一个维度,使得a的维度变成(2,1,3),用如下代码实现
a.unsqueeze(1)#注意形状,在第一维(要注意的是下标是从0开始的)上增加"1"
所以,得到的输出是
tensor([[[0,1,2],
[3,4,5]]])
可以看到输出中a的维度已经变成了(2,1,3)
同样的,如果是需要在倒数第二维上增加一个维度,那么使用如下代码
a.unsqueeze(-2)
调试网络时查看模型每一层的输出尺寸
from torchsummary import sunmmary
summary(your_model,input_size=(channels,H,W))
pip install torchsummary
独热编码
在pytorch中,使用交叉熵损失函数时会自动把label转化为onehot,所以不要手动转化,而使用MSE需要手动转化成onehot编码
import torch
class_num = 8
batch_size = 4
def one_hot(label):
#将一维编码转化为独热编码
labe