Pytorch使用笔记
记录&总结一下自己写model时需要用到的函数。官方文档
pytorch-lightning
torchvision
nn.Module
CNN
torch.nn.Conv1d
torch.nn.Conv1d(in_channels, #int 输入信号的通道
out_channels, # int 卷积产生的通道
kernel_size, # int/tuple 卷积核的尺寸
stride=1, #int/tuple, opt卷积步长
padding=0, #int/tuple, opt 输入的每一条边补充0的层数
dilation=1, #int/tuple, opt 卷积核元素之间的间距
groups=1, #int, opt 从输入通道到输出通道的阻塞连接数
bias=True # bool, opt 如果bias=True,添加偏置)
shape
输入:
(
N
,
C
i
n
,
L
i
n
)
(N,C_{in},L_{in})
(N,Cin,Lin)
weight:
(
C
o
u
t
,
C
i
n
,
k
_
s
)
(C_{out}, C_{in}, k\_s)
(Cout,Cin,k_s) Cout个卷积核并行运算
输出:
(
N
,
C
o
u
t
,
L
o
u
t
)
(N,C_{out},L_{out})
(N,Cout,Lout)
L
o
u
t
=
[
L
i
n
+
2
p
−
d
(
k
_
s
−
1
)
−
1
]
/
s
+
1
L_{out}=[L_{in}+2p-d(k\_s-1)-1]/s+1
Lout=[Lin+2p−d(k_s−1)−1]/s+1
con1d的输入输出的计算方式
Example
# [1,5,7] 卷积核[2,5,3] = [1, 3, 6]
a = torch.ones(1,5,7)
b = nn.Conv1d(in_channels=5, out_channels=3, kernel_size=2)(a)
print(b.size()) #torch.Size([1, 3, 6])
torch.nn.Conv2d
torch.nn.Conv2d(in_channels,
out_channels,
kernel_size,
stride=1,
padding=0,
dilation=1,
groups=1,
bias=True)
参数kernel_size
,stride,padding
,dilation
- 可以是一个int的数据,此时卷积height和width值相同;
- 可以是一个tuple数组,tuple的第一维度表示height的数值,tuple的第二维度表示width的数值
shape
输入:
(
N
,
C
i
n
,
H
i
n
,
W
i
n
)
(N, C_{in}, H_{in}, W_{in})
(N,Cin,Hin,Win)
weight:
(
C
o
u
t
,
C
i
n
,
k
_
s
)
(C_{out}, C_{in}, k\_s)
(Cout,Cin,k_s)
输出:
(
N
,
C
o
u
t
,
H
o
u
t
,
W
o
u
t
)
(N, C_{out}, H_{out}, W_{out})
(N,Cout,Hout,Wout)
H
o
u
t
=
[
H
i
n
+
2
p
[
0
]
−
d
[
0
]
(
k
_
s
[
0
]
−
1
)
−
1
]
/
s
[
0
]
+
1
W
o
u
t
=
[
W
i
n
+
2
p
[
1
]
−
d
[
1
]
(
k
_
s
[
1
]
−
1
)
−
1
]
/
s
[
1
]
+
1
H_{out}=[H_{in}+2p[0]-d[0](k\_s[0]-1)-1]/s[0]+1 \\ W_{out}=[W_{in}+2p[1]-d[1](k\_s[1]-1)-1]/s[1]+1
Hout=[Hin+2p[0]−d[0](k_s[0]−1)−1]/s[0]+1Wout=[Win+2p[1]−d[1](k_s[1]−1)−1]/s[1]+1
torch.nn.ConvTranspose1d
1维的解卷积操作(transposed convolution operator,注意改视作操作可视作解卷积操作,但并不是真正的解卷积操作)
注意
由于内核的大小,输入的最后的一些列的数据可能会丢失。因为输入和输出是不是完全的互相关。因此,用户可以进行适当的填充(padding操作)。
torch.nn.ConvTranspose1d(in_channels,
out_channels,
kernel_size,
stride=1,
padding=0, #输入的每一条边补充0的层数
output_padding=0, #输出的每一条边补充0的层数
groups=1,
bias=True)
shape
输入:
(
N
,
C
i
n
,
L
i
n
)
(N,C_{in},L_{in})
(N,Cin,Lin)
输出:
(
N
,
C
o
u
t
,
L
o
u
t
)
(N,C_{out},L_{out})
(N,Cout,Lout)
L
o
u
t
=
(
L
i
n
−
1
)
s
−
2
p
+
k
_
s
+
o
_
p
L_{out}=(L_{in}-1)s-2p+k\_s+o\_p
Lout=(Lin−1)s−2p+k_s+o_p
变量
- weight(tensor) - 卷积的权重,大小是(in_channels, in_channels,kernel_size)
- bias(tensor) - 卷积的偏置系数,大小是(out_channel)
RNN
LSTM
self.rnn = nn.LSTM(self.ninput, self.nhidden,
self.nlayers, batch_first=True,
dropout=self.drop_prob, bidirectional=self.bidirectional)
self.register_buffer()
回顾模型保存:torch.save(model.state_dict())
,model.state_dict()
是一个字典,里边存着我们模型各个部分的参数。
在model中,我们需要更新其中的参数,训练结束将参数保存下来。但在某些时候,我们可能希望模型中的某些参数参数不更新(从开始到结束均保持不变),但又希望参数保存下来(model.state_dict()
),这是我们就会用到 register_buffer()
参考网址
Loss
BCELoss
torch.nn.BCELoss(weight=None, size_average=True)
计算了输出与target之间的二进制交叉熵
l
o
s
s
(
o
,
t
)
=
−
1
n
∑
i
[
t
[
i
]
l
o
g
(
o
[
i
]
)
+
(
1
−
t
[
i
]
)
l
o
g
(
1
−
o
[
i
]
)
]
loss(o,t)=-\frac{1}{n}\sum_i[t[i] log(o[i])+(1-t[i]) log(1-o[i])]
loss(o,t)=−n1i∑[t[i]log(o[i])+(1−t[i])log(1−o[i])] 如果weight被指定 :
l
o
s
s
(
o
,
t
)
=
−
1
n
∑
i
w
e
i
g
h
t
s
[
i
]
(
t
[
i
]
l
o
g
(
o
[
i
]
)
+
(
1
−
t
[
i
]
)
∗
l
o
g
(
1
−
o
[
i
]
)
)
loss(o,t)=-\frac{1}{n}\sum_iweights[i] (t[i] log(o[i])+(1-t[i])* log(1-o[i]))
loss(o,t)=−n1i∑weights[i](t[i]log(o[i])+(1−t[i])∗log(1−o[i]))
这个用于计算 auto-encoder 的 reconstruction error。注意 0<=target[i]<=1。
默认情况下,loss会基于element平均,如果size_average=False的话,loss会被累加。
torch
torch
.cuda()
cuda放到了gpu上,CPU和GPU的tensor不能直接想加,会报错
gpu
设置gpu
#第一种写法(更推荐)
import os
os.environ[“CUDA_VISIBLE_DEVICES”] = “2”
#第二种写法pytorch
import torch
torch.cuda.set_device(id)
打印当前gpu
其他数据类型转化为torch
numpy->torch
>>> a=np.ones(5)
>>> b=torch.from_numpy(a)
torch->numpy
>>> a = torch.ones(5)
>>> b=a.numpy()
参考:
https://zhuanlan.zhihu.com/p/55419865
torch维度形状变换
开始改模型代码的时候发现模型里的torch有很多维度变换,不熟悉这些函数或者不认真看的话就数据维度就会乱七八糟,自己改的模型很容易跑不起来(在这里反省一下我本科居然读的还是软件工程,真的好不专业TAT)
1.repeat()
参数的个数,不能少于被操作的张量的维度的个数
>>> a = torch.randn(33, 55)
>>> a.size()
torch.Size([33, 55])
>>> a.repeat(1,2).size() # 原始值:torch.Size([33, 55])
torch.Size([33, 110])
>>> a.repeat(1,1,2).size() # 原始值:torch.Size([33, 55])
torch.Size([1, 33, 110])
>>> a.repeat(1,1,1,1).size() # 原始值:torch.Size([33, 55])
torch.Size([1, 1, 33, 55])
2.transpose()和permute()
transpose()
交换指定的两个维度的内容
>>>a = torch.tensor([[[1, 2, 3, 4], [4, 5, 6, 7]], [[7, 8, 9, 10], [10, 11, 12, 13]], [[13, 14, 15, 16], [17, 18, 19, 20]]])
>>>print(a, a.shape)
tensor([[[ 1, 2, 3, 4],[ 4, 5, 6, 7]],
[[ 7, 8, 9, 10],[10, 11, 12, 13]],
[[13, 14, 15, 16],[17, 18, 19, 20]]])
torch.Size([3, 2, 4])
>>>b = a.transpose(1,2)
>>>print(b, b.shape)
tensor([[[ 1, 4],
[ 2, 5],
[ 3, 6],
[ 4, 7]],
[[ 7, 10],
[ 8, 11],
[ 9, 12],
[10, 13]],
[[13, 17],
[14, 18],
[15, 19],
[16, 20]]])
torch.Size([3, 4, 2])
permute()
可以一次性交换多个维度
>>>c = a.permute(2, 1, 0)
>>>print(c, c.shape)
tensor([[[ 1, 7, 13],
[ 4, 10, 17]],
[[ 2, 8, 14],
[ 5, 11, 18]],
[[ 3, 9, 15],
[ 6, 12, 19]],
[[ 4, 10, 16],
[ 7, 13, 20]]]) torch.Size([4, 2, 3])
3.view()
把原先tensor中的数据按照行优先的顺序排成一个一维的数据(地址是连续存储的),然后按照参数组合成其他形状的tensor。(相当于tensorflow里边的reshape)
参数不可为空,参数中的-1就代表这个位置由其他位置的数字来推断,不允许同时有两个-1。
view()不影响传入的tensor
>>> a=torch.Tensor([[[1,2,3],[4,5,6]]])
>>> print(a.view(3,2))
tensor([[1., 2.],
[3., 4.],
[5., 6.]])
4.squeeze()和unsqueeze()
torch.squeeze()
用于对数据的维度进行压缩,去掉维数为1的的维度。squeeze(a)就是将a中所有为1的维度删掉,不为1的维度没有影响。a.squeeze(N) 和 b=torch.squeeze(a,N) 就是去掉a中指定的维数为一的维度。
torch.unsqueeze()
用于对数据维度进行扩充,给指定位置加上维数为一的维度。a.squeeze(N) 和 b=torch.squeeze(a,N) 就是在a中指定位置N加上一个维数为1的维度。
torch分布
- 均匀分布
torch.rand(*sizes, out=None)
→ Tensor
返回一个张量,包含了从区间[0, 1)的均匀分布中抽取的一组随机数。张量的形状由参数sizes定义。
参数:- sizes (int…) - 整数序列,定义了输出张量的形状
- out (Tensor, optinal) - 结果张量
torch.bmm
用来计算两个tensor之间乘积(两个tensor必须都是三维的)
维度有限制,要求啊a,b两个tensor有如下格式:
a:
(
z
,
x
,
y
)
(z,x,y)
(z,x,y)
b:
(
z
,
y
,
c
)
(z,y,c)
(z,y,c)
则result = torch.bmm(a,b),维度为:
(
z
,
x
,
c
)
(z,x,c)
(z,x,c)
torchvision
Pytorch计算机视觉工具包,包含三大模块
torchvision.transforms
: 常用的图像预处理方法
• 数据中心化\quad• 数据标准\quad• 缩放 \quad• 裁剪
• 旋转\quad• 翻转\quad• 填充 \quad• 噪声添加
• 灰度变换\quad• 线性变换\quad• 仿射变换\quad• 亮度、饱和度及对比度变换
torchvision.datasets
: 常用数据集的dataset实现,MNIST,CIFAR-10,ImageNet等
torchvision.models
: 常用的模型预训练,AlexNet,VGG, ResNet,GoogLeNet等
参考:torchvision / torchvision官方文档
torchvision.transforms
self.norm = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
image_transform = transforms.Compose([
transforms.Scale(int(imsize * 76 / 64)),
transforms.RandomCrop(imsize),
transforms.RandomHorizontalFlip()])
1 数据标准化—transforms.Normalize
transforms.Normalize(mean,std,inplace=False)
功能:逐channel的对图像进行标准化
o
u
t
p
u
t
=
i
n
p
u
t
−
m
e
a
n
s
t
d
output =\frac{input-mean}{std}
output=stdinput−mean
• mean:各通道的均值
• std:各通道的标准差
• inplace:是否原地操作
2 transforms.ToTensor
transforms.ToTensor()
将shape为(H, W, C)的nump.ndarray
或img
转为shape为(C, H, W)的tensor
,其将每一个数值归一化到[0,1],其归一化方法比较简单,直接除以255即可。
3 transforms.Scale / transforms.Resize
transforms.Resize(size[, interpolation, max_size, …])
用于对载入的图片数据按需求的大小进行缩放。
size可以是一个整形数据,也可以是一个类似于(h,w)的序列,其中,h代表高度,w代表宽度,但是如果使用的是一个整形数据,那么代表缩放的宽度和高度都是这个整形数据的值。
transforms.Scale()
用于对载入的图片数据按需求进行缩放,和torchvision.transforms.Resize()用法类似。
4 transforms.RandomCrop()
transforms.RandomCrop(size, padding=None, pad_if_needed=False, fill=0, padding_mode='constant')
Crop the given image at a random location.
5 transforms.RandomHorizontalFlip()
transforms.RandomHorizontalFlip(p=0.5)
Horizontally flip the given image randomly with a given probability.
torchvision.models
1 inception_v3
model = models.inception_v3()
url = 'https://download.pytorch.org/models/inception_v3_google-1a9a5a14.pth'
model.load_state_dict(model_zoo.load_url(url))
for param in model.parameters():
param.requires_grad = False
2 VisionTransformer: vit_h_14
ViT_H_14_Weights.IMAGENET1K_SWAG_E2E_V1
Acc@1 88.552
Acc@5 98.694
633.5M
PyTorch Lightning
可以理解为keras之于tensorflow
为了让用户能够脱离PyTorch一些繁琐的细节,专注于核心代码的构建,提供了许多实用工具,可以让实验更加高效
PyTorch Lightning 官方文档
参考教程
PyTorch Lightning初步教程(下) - 知乎
Lightning将大部分AI相关代码分为三个部分:
-
研究代码,主要是模型的结构、训练等部分。被抽象为
LightningModule类
。
研究代码可划分为以下几个组件:- 模型
- 数据处理
- 损失函数
- 优化器
-
工程代码,这部分代码重复性强,比如16位精度,分布式训练。被抽象为
Trainer类
。 -
非必要代码,这部分代码和实验没有直接关系,不加也可以,加上可以辅助,比如梯度检查,log输出等。被抽象为
Callbacks类
。
main函数
import pytorch_lightning as pl
pl.seed_everything(1234) # 这个是用于固定seed用
# args
parser = ArgumentParser()
parser = pl.Trainer.add_argparse_args(parser)
parser = LitClassifier.add_model_specific_args(parser)
parser = MNISTDataModule.add_argparse_args(parser)
args = parser.parse_args()
# data
dm = MNISTDataModule.from_argparse_args(args)
# model
model = LitClassifier(args.hidden_dim, args.learning_rate)
# training
trainer = pl.Trainer.from_argparse_args(args)
trainer.fit(model, datamodule=dm)
result = trainer.test(model, datamodule=dm)
pprint(result)
Trainer
parser = Trainer.add_argparse_args(parser)
将所有可用的trainer选项添加到argparse
其他
torch.backends.cudnn.benchmark
GPU 相关的 flag:在一般场景下,只要简单地在 PyTorch 程序开头将其值设置为 True,就可以大大提升卷积神经网络的运行速度。
- 具体功能:在 PyTorch 中对模型里的卷积层进行预先的优化,也就是在每一个卷积层中测试 cuDNN 提供的所有卷积实现算法,然后选择最快的那个。
- 不能用的场景:如果我们的网络模型一直变的话,那肯定是不能设置 cudnn.benchmark=True 的。因为网络结构经常变,每次 PyTorch 都会自动来根据新的卷积场景做优化:这次花费了半天选出最合适的算法出来,结果下次你结构又变了,之前就白做优化了。不仅如此,还得要根据这个新的结构继续做选择最高效的算法组合,又花费不少的时间。这样反而会大大降低效率。