Snippets for Pytorch
新手码代码时不会之处,资料源于各个博客,参考链接位于最下方
数据集相关
torch.utils.data.DataLoader
DataLoader
是一种可迭代对象,使用iter()
访问,不能使用next()
访问,通过iter()
构建迭代器,然后可以使用next()
访问- 和其他迭代对象一样,
DataLoader
也可以用for
访问,并且一般用for X, y in DataLoader:
进行可迭代对象的访问。 DataLoader
本质上是一个iterable
,并利用多进程来加速batch data
的处理,使用yield
来使用有限的内存。DataLoader
是一个高效,简洁,只管的网络输入数据结构,便于使用和扩展。
pytorch数据加载流程
- 创建一个Dataset对象
- 创建一个DataLoader对象
- 循环这个DataLoader对象,将 img,label加载到模型中训练
伪代码如下(不严格遵守pytorch语法)
1 | dataset = MyDataset(transform=trans) |
DataLoader详解
DataLoader的作用:将自定义的Datset根据batchsize
的大小,是否shuffle
等封装成一个batchsize
大小的tensor
,用于后续的训练。DataLoader(object)
的参数:
- 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)
一般重要的参数有:dataset
, batch_size
, shuffle
, num_workers
, drop_last
自定义数据集
- 自定义的数据集必须继承自
torch.utils.data.Dataset
。torch.utils.data.Dataset
这个类是一个表示数据集的抽象类,负责处理索引(index)到样本(sample)映射的一个类(class)。Pytorch提供两种数据集: Map式数据集 和 Iterable式数据集。这里我们只介绍前者 - 自定义的数据集中必须包含以下两种方法:
__getitem__
和__len__
。其中__getitem__
用于获取索引对应的样本,__len__
方法用于获取样本长度。 - 接下来提供构建的范式
1 | class MyDataset(torch.utils.data.Dataset): |
训练过程相关
初始化:Model.apply(Initial)
为什么要初始化?
- 加快梯度下降的收敛速度
- 更有可能获得一个低模型误差,或者低泛化误差的模型
- 降低因为未初始化或初始化不当导致的梯度消失或者梯度爆炸问题。此情况会导致模型训练速度变慢,崩溃,甚至失败(如全连接层中,如果$W_{ij}$的每一个参数都很大,用Sigmoid作为激活函数,则会导致梯度消失)
- 随机初始化,可以打破对成性,从而保证不同的隐藏单元可以学习到不同的东西。
如何初始化
- 预训练初始化
- 全0初始化(线性回归,Logistics回归)
- 固定值初始化
- BN层,$\gamma$为1,$\beta$为0
- Bias通常用0初始化,对于ReLU激活的神经元,通常采用0.01偏置
- 固定方差初始化
- 高斯分布
- 均匀分布
- 方差放缩初始化
- Xavier初始化(ReLU较差)
- He初始化
代码
weights_init_normal
函数
1 | def weights_init_normal(m): |
- 训练初始化
1 | model.apply(weights_init_normal) |
模型验证相关
一些基本概念
- 在正向传播时,需要求导的变量除了执行
forward()
之外,还会同时为反向传播做一些准备 - 叶子节点:当一个tensor是用户创建时(即用
torch.tensor()
等函数定义的,而非通过计算获得的),他是一个叶子节点,当tensor时由其他运算操作产生时,不是叶子节点 - 在
backward()
之后只有叶子节点相关的导数结果会被保存,而非叶子节点的导数结果会被抛弃(为了减小内存消耗)
中间层检验:hook
在对训练结果进行评判时,我们往往需要对网络的中间结果进行评估从而判断网络的注意力和权重分配等效果,通常有卷积核、特征图、梯度等。卷积核较后二者容易获得,但是特征图一经传递完成就会释放内存从而防止内存冗余,梯度方面,只保存节点梯度。在模型中增加中间结果保存操作可以实现中间结果的,但是会导致速度变慢、操作复杂、训练中产生大量数据等问题。所以需要用其他方法,而hook
则是pytorch提供的一个较好的方法。包括以下函数:Tensor.register_hook(hook_fn)
,nn.Module.register_forward_hook(hook_fn)
,nn.Module.register_backward_hook(hook_fn)
Tensor.register_hook(hook_fn)
功能:注册一个反向传播的hook函数,用于自动记录Tensor的梯度
1
2
3
4
5
6
7 a = torch.Tensor([1,2]).require_grad_()
b = torch.Tensor([1,2]).require_grad_()
d = torch.Tensor([1,2]).require_grad_()
c = a + b
e = c * d
o = e.sum()
o.backward()以上代码中,只有a,b,d为叶子节点,因此只有他们的导数被保留,其他导数,如c,d,e,o无导数,
o.grad
返回为None。下面通过register_hook
方法给cdeo等非叶子节点的张量保留导数。
1
2
3
4 def hook_fn(grad):
print(grad)
e.register_hook(hook_fn)
o.backward()
在对o.backward()
时,自动执行张量e
所关联的hook_fn
,从而直接将e
的导数打印出来总结:
- 在反向传播前,定义一个
hook
函数,描述对梯度的操作,函数名自拟(一般为hook_fn(grad)
),参数只有grad,表示梯度;- 对要获取梯度的张量进行
Tensor.register_hook(hook_fn)
注册- 执行反向传播,执行的过程中自动调用
hook_fn
函数
nn.Module.register_forward_hook(hook_fn)
&nn.Module.register_backward_hook(hook_fn)
这两个操作对象都是
nn.Module
类,如神经网络中的卷积层nn.Conv2d
,全连接层nn.Linear
等。
对于模型的中间模块,也可以视作中间节点(非叶子节点),其输出为特征图或激活值,若要获取,则可以用hook功能
register_forward_hook
是前向传播的输出的,即特征图或激活值,而register_backward_hook
是反向传播输出的,即梯度值
register_forward_hook(hook_fn)
对于hook_fn的定义如下1
2def forward_hook(module, input, output):
operations
module指的是模块,input指的是输入模块的内容,output指的是输出模块的内容。
register_backward_hook(hook_fn)
对于hook_fn的定义如下1
2def backward_hook(module, grad_in, grad_out):
operations
值得注意的是这里的grad_in
和grad_out
,这里的in和out都是相对于forward传递而言的。如线性模块$o=W\times x+b$,grad_out
指的就是o的梯度;而grad_in因为有三个变量,因此他是包含三个元素的tuple
例子
1 | import torch |