Pytorch中比较好用的API
本专栏主要介绍Pytorch中比较好用的API
nn.Flatten()
torch.nn.Flatten(start_dim=1, end_dim=-1)
作用:将连续的维度范围展平为张量
参数:开始维度,结束维度
input = torch.randn(32, 1, 5, 5) # 随机数 |
next()和iter()
经常会遇到next和iter联合使用,一般用于取数据时:X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
iter(可迭代对象)
补充:可迭代对象Iterable:
- 一类是:list、tuple、dict、set、str
- 二类是:generator(都是Iterator对象),包含生成器和带yield的generator function
生成器不但可以作用于for,还可以被next函数不断调用并且返回下一个值,可以被next函数不断调用返回下一个值的对象称为迭代器(Iterator)。可迭代的对象如list、dict等需要用iter()函数转化成Iterator
next(iterator[, default])
- iterator –可迭代对象
- default –可选,用于设置在没有下一个元素时返回该默认值,如果不设置,又没有下一个元素则会触发 StopIteration 异常
list_ = [1, 2, 3, 4, 5] |
layer.__class__.__name__
用于调试神经网络时,可以打印出网络每层的名称和形状
X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32) |
summary()
类似tensorflow中的summary,可以显示使用pytorch写的网络结构
pip install torchsummary
import torch |
# 输出 |
net.fc和net.fc.weight
net是实例化后的网络,fc是网络中某层的名称
net.fc
和net.fc.weight
分别是直接访问net的某层和该层的权重,可以直接通过赋值来修改层和参数
finetune_net = torchvision.models.resnet18(pretrained=True) # 参数表示不仅定义模型,而且取出在ImageNet上训练好的参数 |
net.children()
net是实例化后的网络,children()方法是提取出该对象的所有层,通常的用法:
list(net.children())[-3:] # 取出网络的最后三层 |
net.add_module()
网络添加层
net.add_module('transpose_conv', nn.ConvTranspose2d(num_classes, num_classes, kernel_size=64, padding=16, stride=32)) |
tensor.detach()
通常用在冻结网络部分结构
返回一个新的tensor
,从当前计算图中分离下来的,但是仍指向原变量的存放位置,不同之处只是requires_grad为false,得到的这个tensor
永远不需要计算其梯度,不具有grad。即使之后重新将它的requires_grad置为true,它也不会具有梯度grad
这样我们就会继续使用这个新的tensor进行计算,后面当我们进行反向传播时,到该调用detach()的tensor
就会停止,不能再继续向前进行传播
注:使用detach返回的tensor
和原始的tensor
共同一个内存,即一个修改另一个也会跟着改变
import torch |
import torch |
从上可见tensor c是由out分离得到的,但是我也没有去改变这个c,这个时候依然对原来的out求导是不会有错误的,即c,out之间的区别是c是没有梯度的,out是有梯度的,但是需要注意的是下面两种情况是汇报错的
① 当使用detach()分离tensor,然后用这个分离出来的tensor去求导数,会影响backward(),会出现错误
② 当使用detach()分离tensor并且更改这个tensor时,即使再对原来的out求导数,会影响backward(),会出现错误
torch.cat()
2, 3) x = torch.randn( |
torch.squeeze()和torch.unsqueeze()
torch.squeeze(A, N)
减少张量A指定位置N的维度,如果张量A维度为(1, 1, 3),执行torch.squeeze(A, 1)
后A的维度变为(1, 3)
如果指定的维度大于1,那么操作无效
如果不指定维度N,那么将删除所有维度为1的维度
torch.unsqueeze(A, N)
增加数组A指定位置N的维度,例如两行三列的数组A维度为(2,3),那么这个数组就有三个位置可以增加维度,分别是( [位置0] 2,[位置1] 3 [位置2] )或者是 ( [位置-3] 2,[位置-2] 3 [位置-1] ),如果执行 torch.unsqueeze(A,1),数据的维度就变为了 (2,1,3)
.to(device)
torch.tensor的本质是对象,所有的tensor对象都具有这个方法,可将tensor数据放入gpu上运行
torch中定义的变量、常量默认都在cpu上,如果想放在GPU上,就需要data.to(device)
来指明
将GPU上的数据搬回CPU,可以使用.to(device)
或者直接data.cpu()
,第二种方式需要注意data必须是no_grad的状态,可以使用with torch.no_grad():
来指明
device的常用写法:device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
Module类
注意,在Pytorch中,to the best of my knowledge,torch.nn
提供的所有模块、网络都是继承nn.Module
,这意味着在Pytorch中没有像Tensorflow中的layer概念,直接是全部的Module拼接在一起,那么我们可以选择性地保存部分Module参数
在Pytorch中,只有继承自Module类的子类,程序才会自动地、默认地处理batch这个维度,我们只需要专心对除了batch以外的维度进行操作即可,因为Module类帮我们处理了batch
复数处理
在Pytorch中,如果需要在网络中使用到复数的相关计算,会提示无法实现复数的微分,因此涉及到复数处理部分,可以使用with torch.no_grad():
让这部分 不用计算梯度,处理完成后,再为某tensor数据使用data.requires_grad_(True)
方法,使下面的操作再次需要计算梯度。这意味着只要父类数据需要计算梯度或者父类数据处于GPU上,那么由它计算得出的子类数据也都是需要计算梯度并且处于GPU上的
但是请注意!!!上述处理方法尽可能少使用,因为如果网络需要grad的变量之间相隔较远,会极大影响网络性能,甚至导致不收敛!
torch.nn.ConvTranspose2d
使用前提:stride > 1
是通过padding使得卷积之后输出的特征图大小保持不变(相对于输入特征图),不代表得到的输出特征图的大小与输入特征图的大小完全相同,而是他们之间的比例保持为 输入特征图大小/输出特征图大小 = stride
比如输入特征图为66,stride=2, kernel_size = 3, 所以进行same卷机操作得输出特征图为33 (6/2 = 3)
如果输入特征图为55,stride=2,kernel_size = 3,这时候设置padding = 1,那么也会得到输出特征图为33
那么这样的情况就会导致在逆卷积时出现一个问题。
问题就是,不同大小的图片经过卷积运算能得到相同尺寸的输出,那么作为逆运算,同样的一张输入图像经过反卷积是否会有不同尺寸的合法输出?这样的话就存在争议了
上面还只是进行same卷积的情况,如果考虑valid卷积,stride=2, kernel_size = 3,padding=0时,输入特征图为77和88的结果也是3*3
解决争议的办法就是使用output_padding参数
output_padding的作用是:
当stride > 1时,Conv2d将多个输入形状映射到相同的输出形状。output_padding通过在一边有效地增加计算出的输出形状来解决这种模糊性。
首先我们要认同一个前提:
**大多数情况下我们都希望经过卷积/反卷积处理后的图像尺寸比例与步长相等,即**输入特征图大小/输出特征图大小 = stride**,也就是same模式**。
所以我们只要通过添加output_padding这一参数来使得结果满足这一前提,那么输出的图片的大小就能够保证为输入图片*stride的大小,而不是任意可能的大小
实现办法:
因为pytorch将参数padding(注意与output_padding区别)建议设置为(kernel_size - 1)/2,由式子padding= kernel - 1 - padding转换而来
当我们希望得到****输入特征图大小/输出特征图大小 = stride****的话,代入上面的式子能够得到结果:
padding = (kernel_size - stride + output_padding )/2
所以为了让padding = (kernel_size - 1)/2,则output_padding应该取值为stride - 1,这样就能够满足*输入特征图大小/输出特征图大小 = stride*
当然,你可以取别的值,这并不会影响到逆卷积的计算,但是在后面进行有关大小的操作时就很可能出现问题,因为输出的图片的大小并不能保证是 输入图片stride的大小,可能是任意正确的大小,如上面举的例子,可能是77或8*8等
torch.einsum()
ein 就是爱因斯坦的ein,sum就是求和。einsum就是爱因斯坦求和约定,其实作用就是把求和符号省略
https://www.youtube.com/watch?v=pkVwUVEHmfI
torch.permute()
用于交换tensor维度,和transpose很像,但是它一步到位
a = torch.tensor([[1,2],[3,4]]) |