Pytorch 自定义Function
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pytorch 自定义Function相关的知识,希望对你有一定的参考价值。
参考技术A https://zhuanlan.zhihu.com/XavierLin在本次,我们将学习如何自定义一个torch.autograd.Function,下面是本次的主要内容
1. 对Function的直观理解;
2. Function与Module的差异与应用场景;
3. 写一个简单的ReLU Function;
在之前的介绍中,我们知道,Pytorch是利用Variable与Function来构建计算图的。回顾下Variable,Variable就像是计算图中的节点,保存计算结果(包括前向传播的激活值,反向传播的梯度),而Function就像计算图中的边,实现Variable的计算,并输出新的Variable。Function简单说就是对Variable的运算,如加减乘除,relu,pool等。但它不仅仅是简单的运算。与普通Python或者numpy的运算不同,Function是针对计算图,需要计算反向传播的梯度。因此他不仅需要进行该运算(forward过程),还需要保留前向传播的输入(为计算梯度),并支持反向传播计算梯度。如果有做过公开课cs231的作业,记得里面的每个运算都定义了forward,backward,并通过保存cache来进行反向传播。这两者是类似的。在之前Variable的学习中,我们知道进行一次运算后,输出的Variable对应的creator就是其运行的计算,如y = relu(x), y.creator,就是relu这个Function。我们可以对Function进行拓展,使其满足我们自己的需要,而拓展就需要自定义Function的forward运算,以及对应的backward运算,同时在forward中需要通过保存输入值用于backward。总结,Function与Variable构成了pytorch的自动求导机制,它定义的是各个Variable之间的计算关系。
2. Function与Module的差异与应用场景
Function与Module都可以对pytorch进行自定义拓展,使其满足网络的需求,但这两者还是有十分重要的不同:
1)Function一般只定义一个操作,因为其无法保存参数,因此适用于激活函数、pooling等操作;Module是保存了参数,因此适合于定义一层,如线性层,卷积层,也适用于定义一个网络。
2)Function需要定义三个方法:__init__, forward, backward(需要自己写求导公式);Module:只需定义__init__和forward,而backward的计算由自动求导机制构成。
3)可以不严谨的认为,Module是由一系列Function组成,因此其在forward的过程中,Function和Variable组成了计算图,在backward时,只需调用Function的backward就得到结果,因此Module不需要再定义backward。
4)Module不仅包括了Function,还包括了对应的参数,以及其他函数与变量,这是Function所不具备的
1)首先我们定义一个继承Function的ReLU类;
2)然后我们来看Variable在进行运算时,其creator是否是对应的Function;
3)最后我们为方便使用这个ReLU类,将其wrap成一个函数,方便调用,不必每次显式都创建一个新对象;
3.1 定义一个ReLU类
3.2 验证Variable与Function的关系
输出:
可见,Function连接了Variable与Variable,并实现不同计算。
3.3 Wrap一个ReLU函数
可以直接把刚才自定义的ReLU类封装成一个函数,方便直接调用
输出:
Pytorch 自定义数据加载器
【中文标题】Pytorch 自定义数据加载器【英文标题】:Pytorch customized dataloader 【发布时间】:2021-11-09 05:29:23 【问题描述】:我正在尝试使用 pytorch-lightening 训练具有 MNIST 数据集的分类器。
import pytorch_lightning as pl
from torchvision import transforms
from torchvision.datasets import MNIST, SVHN
from torch.utils.data import DataLoader, random_split
class MNISTData(pl.LightningDataModule):
def __init__(self, data_dir='./', batch_size=256):
super().__init__()
self.data_dir = data_dir
self.batch_size = batch_size
self.transform = transforms.ToTensor()
def download(self):
MNIST(self.data_dir, train=True, download=True)
MNIST(self.data_dir, train=False, download=True)
def setup(self, stage=None):
if stage == 'fit' or stage is None:
mnist_train = MNIST(self.data_dir, train=True, transform=self.transform)
self.mnist_train, self.mnist_val = random_split(mnist_train, [55000, 5000])
if stage == 'test' or stage is None:
self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform)
def train_dataloader(self):
mnist_train = DataLoader(self.mnist_train, batch_size=self.batch_size)
return mnist_train
def val_dataloader(self):
mnist_val = DataLoader(self.mnist_val, batch_size=self.batch_size)
return mnist_val
def test_dataloader(self):
mnist_test = DataLoader(self.mnist_test, batch_size=self.batch_size)
使用MNISTData().setup()
后,我得到了MNISTData().mnist_train, MNISTData().mnist_val, MNISTData().mnist_test
,长度分别为55000、5000、10000,类型为torch.utils.data.dataset.Subset。
但是当我调用 dataloader w.r.t MNISTData().train_dataloader, MNISTData().val_dataloader, MNISTData().test_dataloader
时,我只得到包含 215、20、None 数据的 DataLoader。
有人可以知道原因或可以解决问题吗?
【问题讨论】:
返回215, 20, None
的代码在哪里?顺便说一句,test_dataloader(...)
中没有 return
。
在更正 return
的 test_dataloader()
之后,我仍然有问题。
a = MNISTData()
a.setup()
b,c,d = a.train_dataloader(), a.val_dataloader(),a.test_dataloader()
你能试试上面的代码并检查变量吗?
【参考方案1】:
正如我在 cmets 中所说,以及 Ivan 在他的回答中发布的,缺少 return 声明:
def test_dataloader(self):
mnist_test = DataLoader(self.mnist_test, batch_size=self.batch_size)
return mnist_test # <<< missing return
根据您的评论,如果我们尝试:
a = MNISTData()
# skip download, assuming you already have it
a.setup()
b, c, d = a.train_dataloader(), a.val_dataloader(), a.test_dataloader()
# len(b)=215, len(c)=20, len(d)=40
我认为您的问题是为什么 b, c, d
的长度与数据集的长度不同。答案是DataLoader
的len()
等于批次数,而不是样本数,因此:
import math
batch_size = 256
len(b) = math.ceil(55000 / batch_size) = 215
len(c) = math.ceil(5000 / batch_size) = 20
len(d) = math.ceil(10000 / batch_size) = 40
顺便说一句,我们使用math.ceil
,因为DataLoader
默认有drop_last=False
,否则它将是math.floor
。
【讨论】:
【参考方案2】:您的test_dataloader
函数缺少return
语句!
def test_dataloader(self):
mnist_test = DataLoader(self.mnist_test, batch_size=self.batch_size)
return mnist_test
>>> ds = MNISTData()
>>> ds.download()
>>> ds.setup()
然后:
>>> [len(subset) for subset in \
(ds.mnist_train, ds.mnist_val, ds.mnist_test)]
[55000, 5000, 10000]
>>> [len(loader) for loader in \
(ds.train_dataloader(), ds.val_dataloader(), ds.test_dataloader())]
[215, 20, 40]
【讨论】:
【参考方案3】:其他人指出您缺少return
是test_dataloader()
的事实当然是正确的。
从问题的框架来看,您似乎对Dataset
和DataLoader
的长度感到困惑。
len(Dataset(..))
返回数据集中的数据样本数。
而len(DataLoader(ds, ...))
返回批次数;这取决于您请求了多少batch_size=...
,是否要drop_last
批处理等。确切的计算由@Berriel 正确提供
【讨论】:
以上是关于Pytorch 自定义Function的主要内容,如果未能解决你的问题,请参考以下文章