解决pytorch中Dataloader读取数据太慢的问题

这篇具有很好参考价值的文章主要介绍了解决pytorch中Dataloader读取数据太慢的问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

最近在使用pytorch框架进行模型训练时遇到一个性能问题,即数据读取的速度远远大于GPU训练的速度,导致整个训练流程中有大部分时间都在等待数据发送到GPU,在资源管理器中呈现出CUDA使用率周期性波动,且大部分时间都是在等待数据加载。


一、造成的原因

其实从前言中就可以知道,造成这样的原因可以理解为:GPU的算力与数据加载速度之间的不匹配

二、查找不匹配的原因

本人使用的GPU为RTX3060,数据集为cifar10,使用的模型为VGG,显然这张显卡对于这个任务来说是绰绰有余的,无论是显存还是算力。
但是几经测试发现,数据从内存送到GPU的速度实在是太慢了,去百度了很久都没有很好的解决。那回到这个问题的本身,既然是数据加载导致的性能差,那问题就出在pytorch的datasetdataloader中。

在dataset中,会将数据从磁盘读入内存中,如果启用了dataloader中的pin_memory,就会让数据常驻内存,同时设置num_workers还能实现多进程读取数据,但即使设置了这些,数据加载速度依然没有质的提升。

博主发现,dataset中的transform是导致性能慢的一个原因,dataset中有个函数为__getitem__,每获取一个数据就会让这个数据过一次transform。相信大家都写过如下的代码:

transform = transforms.Compose([
	transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize([0.4914, 0.4822, 0.4465], [0.5, 0.5, 0.5])
])

但是这里的ToTensor和Normalize完全没必要没读一次数据都处理一次,可以在数据加载到内存的时候就直接全部处理完,这样每个数据只需要经历一次ToTensor和Normalize,这会大大提高数据读取速度,大家可以自行测试一次ToTensor和Normalize所需要的时间,还是非常大的。

在训练的过程中,相信大家也写过如下代码:

for x, y in dataloader:
	x, y = x.cuda(), y.cuda()

经过博主测试,将数据发送到GPU也是非常耗时的,那为什么不一次性全部加载到GPU里面呢?当然前提是你的GPU显存够大。

三、解决方法

以上分析可以总结为两点:

  1. 数据的预处理有一部分可以提前对全部数据做一遍;
  2. 如果显存足够,可以将数据全部加载到GPU中。

基于此,我们可以重载类,这里以pytorch自带的cifar10为例:

class CUDACIFAR10(CIFAR10):
    def __init__(
            self, 
            root: str, 
            train: bool = True,
            to_cuda: bool = True,
            half: bool = False,
            pre_transform: Optional[Callable] = None,
            transform: Optional[Callable] = None, 
            target_transform: Optional[Callable] = None, 
            download: bool = False) -> None:

        super().__init__(root, train, transform, target_transform, download)
        if pre_transform is not None:
            self.data = self.data.astype("float32")
            for index in range(len(self)):
                """
                ToTensor的操作会检查数据类型是否为uint8, 如果是, 则除以255进行归一化, 这里data提前转为float,
                所以手动除以255.
                """
                self.data[index] = pre_transform(self.data[index]/255.0).numpy().transpose((1, 2, 0))
                self.targets[index] = torch.Tensor([self.targets[index]]).squeeze_().long()
                if to_cuda:
                    self.targets[index] = self.targets[index].cuda()
            self.data = torch.Tensor(self.data).permute((0, 3, 1, 2))
            if half:
                self.data = self.data.half()
            if to_cuda:
                self.data = self.data.cuda()

    def __getitem__(self, index: int) -> Tuple[Any, Any]:
        """
        Args:
            index (int): Index

        Returns:
            tuple: (image, target) where target is index of the target class.
        """
        img, target = self.data[index], self.targets[index]

        if self.transform is not None:
            img = self.transform(img)
        if self.target_transform is not None:
            target = self.target_transform(target)
        return img, target

to_cuda为True就会让数据全部加载到GPU中,后续就不需要写x, y = x.cuda(), y.cuda()了。
pre_transform就是让所有数据提前进行的处理,例如使用ToTensor和Normalize,后续调用时不会再进行这些处理。
transform为后续调用时会进行的处理,一般就是一些随机处理过程。

在博主的测试过程中发现,解决了以上问题后,一个epoch只要2秒就能完成,而平时需要15秒,并且任务管理器中的CUDA几乎全程拉满。唯一的代价就是显存占用更高了,这何尝不是一种空间换时间。

四、使用方法

这里直接粘贴我为这个类写的注释

- 使用pytorch自带的CIFAR10时, 每读取一个数据都会调用一次transforms, 其中ToTensor()和Normalize()会消耗巨大的时间
  如果你的数据集非常的大, 那么一个epoch将会花费非常多的时间用于读取数据, 如果还要将数据送入GPU, 那么时间将会继续增加。

- 一般的写法如下:

    for epoch in range(epochs):
        for x, y in dataloader:
            x, y = x.cuda(), y.cuda()

  如果你的数据集很大并且GPU算力很强, 那么读取数据并发送的GPU将会成为性能瓶颈。

- CUDACIFAR10是专门针对pytorch的CIFAR10进行优化的, 使用的前提是你的显存足够的大, 至少8GB, 且读取数据已经是性能瓶颈。
  CUDACIFAR10的参数与CIFAR10非常相似, 新增的参数为:
    to_cuda: bool, 是否将数据集常驻GPU, default=True
    half: bool, 进一步降低数据所占据的显存, 在混合精度训练时使用, 否则可能存在意外(例如梯度值overflow)
    pre_transform: 传入一个transforms, 如果不为None, 则会在初始化数据时直接对所有数据进行对应的转换, 在后续调用时将
                    不会使用pre_transform. 可以将ToTensor()和Normalize()作为pre_transform, 这会大幅度减少读取时间.

- CUDACIFAR10的用法如下:

    pre_transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize([0.4914, 0.4822, 0.4465], [0.5, 0.5, 0.5])
    ])
    dataset = CUDACIFAR10(..., to_cuda=True, pre_transform=pre_transform, ...)
    dataloader = Dataloader(dataset, ..., pin_memory=False, num_workers=0, ...)
    ...
    for epoch in range(epochs):
        for x, y in dataloader:
            # 不需要写x, y = x.cuda(), y.cuda(), 除非to_cuda=False
            ...

- 使用CUDACIFAR10需要注意如果启用了to_cuda, 那么Dataloader不能启用pin_memory, pin_memory是将数据常驻内存, 这会产生冲突.
  同时num_workers=0.

- 如果参数to_cuda=False, pre_transform=None, 那么该类与CIFAR10用法完全一致.

后言

本文写作仓促,可能有部分错误,欢迎大家的批评与指正。文章来源地址https://www.toymoban.com/news/detail-410887.html

到了这里,关于解决pytorch中Dataloader读取数据太慢的问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 全网详细解决git官网下载太慢的问题

    我们在开发的过程中,无可避免地要使用到 git ,来托管我们的代码。 它是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 使用 git 的前提是要下载和安装它,因而,我们可以点击它的官网链接地址:https://git-scm.com。 打开官网链接后,如果你的

    2024年01月23日
    浏览(73)
  • pytorch进阶学习(二):使用DataLoader读取自己的数据集

    上一节使用的是官方数据集fashionminist进行训练,这节课使用自己搜集的数据集来进行数据的获取和训练。 教学视频:https://www.bilibili.com/video/BV1by4y1b7hX/?spm_id_from=333.1007.top_right_bar_window_history.content.clickvd_source=e482aea0f5ebf492c0b0220fb64f98d3 pytorch进阶学习(一):https://blog.csdn.net/w

    2024年02月09日
    浏览(41)
  • Git 安装并初始化 + 官网下载速度太慢的问题

    目录 1. 快速下载 2. 初始化 当你兴致勃勃地去官网下载 git 的时候,突然发现,嗯??下载完成还需 9 个小时? 快速下载点这里! 打开之后是这个样子:  我们可以自行选择下载版本,并点击,然后选择 64 位的 exe,开始下载: (1)右击以管理员身份执行 exe 文件  (2)填

    2024年02月11日
    浏览(49)
  • PlatformIO在Vscode中创建工程太慢的解决办法

    在Vscode中创建PlatformIO的Esp32工程,会始终停留在Please Wait…,尝试用如下两种解决方案 打开PlatformIO CLI,在右侧打开的命令行中输入pio home,会在默认浏览器中打开PlatformIO网页版,按步骤创建ESP32项目即可。 手动创建工程文件夹,打开终端,命令行执行 注意:vscode中安装的

    2024年02月07日
    浏览(50)
  • git clone拉取GitHub项目失败/太慢的解决方法

    针对 在Ubuntu中通过git clone从GitHub上拉取项目时,遇到失败/速度慢等情况, 记录本人的解决思路。 方案一、使用gitclone gitclone.com是一个提供下载缓存的代码下载网站,使用方法简单,只需要 在仓库地址前面加上 gitclone.com ,就可以使速度提升几倍。(个人觉得速度很慢) 例

    2024年04月27日
    浏览(68)
  • centos虚拟机使用docker下载镜像太慢的解决办法

    虚拟环境: 1、VMware Workstation 16 Pro 2、CentOS 7(CentOS Linux release 7.9.2009 (Core),内核版本3.10.0-1160.el7.x86_64) 问题描述:        虚拟机可以与物理主机互相Ping通,也可以Ping通百度,但是总是莫名其妙断网,下载docker镜像几乎很难(已经配置了镜像源,但是下载速度非常非常慢,

    2024年04月11日
    浏览(37)
  • git submodule update --init命令速度太慢的解决方法

    我们在 clone 某个代码库时,代码库可能使用了其他代码库的资源,开发者为避免重复开发,将使用到的其他库以链接的方式作为公共资源保存,我们需要在 clone 之后执行以下命令才能得到完整的依赖: 但是在执行 git submodule update --init 命令时,由于没有提示条,且看不到实

    2024年02月12日
    浏览(60)
  • Ubuntu/Linux/Win系统中git clone速度太慢的解决办法

    在http://tool.chinaz.com/dns中查询下面三个域名: 进入终端命令行模式,输入sudo gedit /etc/hosts 在文件最后添加相应的IP和域名。如下:记录更换 你查询的IP哦! 完成!

    2024年02月08日
    浏览(46)
  • SSH连接下Git fetch/pull/push 速度太慢的一些解决办法

    有时候某些git的代码库在fetch远端的时候会非常慢,速度只有几十k或者几k。这个速度拉取一整个代码库可能要花费一晚上或者一天时间。甚至在每天更新已有代码库时候也会花上十分钟。可以尝试一下这些方法来提升拉取远端代码的速度: 1. Git 版本过低的话可能会导致这个

    2024年02月10日
    浏览(53)
  • 【深度学习】torch.utils.data.DataLoader相关用法 | dataloader数据加载器 | pytorch

    dataloader数据加载器属于是深度学习里面非常基础的一个概念了,基本所有的图像项目都会用上,这篇博客就把它的相关用法总结一下。 之所以要写这篇,是想分清楚len(data_loader)和len(data_loader.dataset) 这里加载的数据我们以Mnist手写数据集为例子 torchvision.datasets.MNIST是用来加载

    2024年02月16日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包