【大鲸洗源码】【驾考科目源码】【源码精灵游戏视频】dataloader源码

2024-11-19 05:25:28 来源:himall v2.4 源码 分类:综合

1.3. torch.utils里需要掌握的源码函数
2.Seed Everything - 可复现的 PyTorch(一)
3.Pytorch中的Dataset和DataLoader源码深入浅出
4.retinanet 网络详解
5.PyTorch - DataLoader 源码解析(一)
6.MMDetection3D之DETR3D源码解析:整体流程篇

dataloader源码

3. torch.utils里需要掌握的函数

       在深度学习框架PyTorch中,torch.utils模块提供了许多实用工具,源码帮助我们有效地处理和加载数据。源码其中几个关键组件包括:

       1. DataLoader:这是源码数据加载的核心工具,它封装了Dataset类,源码使得我们可以并行加载和处理数据,源码大鲸洗源码提高训练效率。源码使用DataLoader时,源码要特别注意add方法的源码运用。

       2. Dataset:有Map-style的源码TensorDataset,它允许我们将数据和标签打包成Tensor,源码便于在索引过程中同时获取数据和对应的源码标签。源代码如下:

       python

        dataset = TensorDataset(data,源码 labels)

       3. IterableDataset:例如IterableDataset,其加载数据的源码方式更像迭代器,适用于需要逐批处理的源码数据源。同样,add方法在使用时也需要注意:

       python

        iterable_dataset = IterableDataset()

        iterable_dataset.add(...)

       4. ConcatDataset和ChainDataset:前者用于连接多个Dataset,后者则适用于连接多个IterableDataset,方便处理多源数据集。

       5. Subset:用于从一个Dataset中提取指定索引序列的子集,这对于数据增强或者验证集划分非常有用。

       通过熟练掌握这些torch.utils中的函数,我们可以更有效地组织和处理数据,提高模型训练的灵活性和性能。

Seed Everything - 可复现的 PyTorch(一)

       为了保证实验的可复现性,许多机器学习的代码都会有一个方法叫seed everything,这个方法尝试固定随机种子以让一些随机的过程在每一次的运行中产生相同的结果。

       什么是随机种子?随机数,分为真随机数和伪随机数,真随机数需要自然界中真实的随机物理现象才能产生,而对于计算机来说生成这种随机数是很难办到的。而伪随机数是通过一个初始化的值,来计算来产生一个随机序列,如果初始值是不变的,那么多次从该种子产生的随机序列也是相同的。这个初始值一般就称为种子。

       Linux系统中的随机数,在Ubuntu系统中,有一个专门管理随机种子的服务systemd-random-seed.service,该服务负责在计算机启动的时候,从硬盘上加载一个随机种子文件到内核中,以作为随机初始化值在整个系统运行的过程中提供服务。Linux会通过许多硬件信息来获得这个初始化值。可以通过/dev/urandom文件来产生随机字节,然后使用od命令(该命令可将字节转换成希望的格式并打印)来获得随机数:

       如果仅希望获得随机数,直接读取/dev/urandom或调用Linux系统调用getrandom()(内部也使用/dev/urandom)是不错的选择。但这种随机数是无法复现的,因为种子是由系统设置的,并且每次开机设置的种子都不一样。在“可复现”的场景中,我们需要的是一种能手动控制随机种子和读取随机序列的方式,以便可以重复获得相同随机序列的功能。

       如果一个过程依赖系统产生的驾考科目源码随机数,则称这个过程是Non Deterministic(不确定的);相反的如果一个过程对相同的输入种子都有相同的输出,则这个随机过程是Deterministic的。在“可复现”场景中,我们需要保证所有的随机过程都是Deterministic的。

       /dev/random可生成“随机性”更强的随机数,但由于其依赖的系统资源更多,导致性能缓慢,因此绝大多数场景都只使用/dev/urandom。

       程序中的随机数,在PyTorch中,设置随机种子的方法是torch.manual_seed(),这里就是我们所设置的随机种子,设置完毕后,如果多次调用同样的具有随机过程PyTorch方法,就会获得相同的结果,例如下面的代码在多次调用后的打印是一样的:

       不论在任何机器或系统,只要使用torch==1..0版本(其他版本大概率也是OK的),输出应该都是长这样的。诶?既然随机种子产生跟系统硬件信息相关,那不同的机器至少应该不一样才对呀?上文说了,在要求“可复现”的场景下,是不能使用/dev/urandom来产生随机数的,那剩下的是需要搞清楚PyTorch是如何生成随机数的。

       通过torch.manual_seed方法往下找,可以知道PyTorch生成随机数是使用了MT(梅森旋转)算法,这个算法的输入只有一个初始化值也不需要其他的环境信息。因此无论在任何机器,只要PyTorch的版本一致(算法部分没有改变)并且设置了随机种子,那么调用随机过程所产生的随机数就是一致的。C++ 在标准库中直接引入了这个方法:std::mt,而PyTorch是自己实现的,官方称性能比C++的版本要更好一些,感兴趣的话可以直接看PyTorch源码。

       NumPy的np.random.seed也同样使用MT来生成随机数,因此也与硬件无关。要注意的是:np.random.seed只影响NumPy的随机过程,torch.manual_seed也只影响PyTorch的随机过程。通过下面的代码很容易验证这个结果:

       由此可以得到这样的程序中所有依赖MT算法产生随机数的包,都需要手动设置随机种子,才能使整个程序的随机性是可复现的。

       “根据文档,设置torch.manual_seed是对所有的设备设置随机种子。目前似乎没有单独为CPU设备设置随机种子的方法。”

       CUDA的随机数,PyTorch中,还有另一个设置随机种子的方法:torch.cuda.manual_seed_all,从名字可知这是设置显卡的随机种子。

       在PyTorch的内部,使用CUDA Runtime API提供的curand来设置随机种子,根据curand的文档,他们提供的所有随机数生成算法都是Deterministic的。

       上面的源码精灵游戏视频代码看起来不够“随机”,因为在不同的GPU设备上产生了相同的结果,如果希望不同设备可以产生不同的随机数,可以这么做:

       上面的代码既保证了随机性(不同设备产生不同的随机数),也保证了确定性(多次调用只产生相同结果)。在真实场景中,一般只会用相同的设备来产生随机数,因此torch.manual_seed()应该就能满足大多数需求。

       不同设备之间的随机数,先问一个问题:“用GPU训练的实验结果,可以在CPU上复现吗?”。

       答案是“也许可以”。

       根据前文可知,CPU设置随机种子是用PyTorch官方实现的MT,而GPU是用到了CUDA Runtime API的curand。因此两套实现是完全不同的,那么对于相同的随机种子,理应产生不同的随机序列,用下面的代码可以验证:

       从上面的例子中知道,对于同一个随机种子,在CPU和GPU上产出的结果是不同的,因此这种情况在GPU上的结果是无法在CPU上复现的。那为什么答案是“也许可以”呢?

       因为很多代码,都会在CPU上创建Tensor,再切换到GPU上。只要不直接在GPU上创建随机变量,就可以避免这个问题。请看下面的例子:

       上面的代码输出值跟CPU一致,但是device是在CUDA上。这样写可能性能不如直接在GPU上直接创建随机变量,但为了保证程序的确定性,牺牲一点性能我认为是值得的。

       多进程的随机性,PyTorch的torch.utils.data.DataLoader在num_worker>0的情况下会fork出子进程,而通常又会在加载数据的时候做很多“随机变换”,那么就有必要讨论一下多进程下的随机性是怎样的,

       子进程一般会保留父进程的一些状态,这也包括随机种子。因此若不做特殊处理,所有子进程都会产生和父进程相同的随机序列。请看下面的例子:

       可以发现两次batch输出的结果是一样的,这是因为主进程中numpy的随机性,被两个worker保留了,因此两个worker的随机性是相同的。

       “上面的结果在torch>=1.9.0是不能复现的,因为PyTorch 1.9之后DataLoader默认会给每个worker重新设置随机种子。”

       这里我们需要为每一个worker设置不同的随机种子以保证随机性,但每次运行又必须要设置相同的随机种子来保证确定性,更好的代码实现如下:

       这段代码将主进程的随机种子设置为,两个worker分别设置为和。因为每次运行随机种子的值是一样的,因此可以保证确定性,另外每一个worker包括主进程的开发快递api源码随机种子都不一样,因此随机性也保证了。

       类似的,对于分布式训练,也需要做类似的操作,这里考虑单机多卡的情况:

       假设有两个GPU进行训练,那么第一个GPU的主进程和两个worker进程的seed为:,,;第二块GPU是:,,。

       Seed Everything,最后点题,祭出一个seed_everything:

Pytorch中的Dataset和DataLoader源码深入浅出

       构建Pytorch中的数据管道是许多机器学习项目的关键步骤,尤其是当处理复杂的数据集时。本篇文章将深入浅出地解析Pytorch中的Dataset和DataLoader源码,旨在帮助你理解和构建高效的数据管道。

       如果你在构建数据管道时遇到困扰,比如设计自定义的collate_fn函数不知从何入手,或者数据加载速度成为训练性能瓶颈时无法优化,那么这篇文章正是你所需要的。通过阅读本文,你将能够达到对Pytorch中的Dataset和DataLoader源码的深入理解,并掌握构建数据管道的三种常见方式。

       首先,我们来了解一下Pytorch中的Dataset和DataLoader的基本功能和工作原理。

       Dataset是一个类似于列表的数据结构,具有确定的长度,并能通过索引获取数据集中的元素。而DataLoader则是一个实现了__iter__方法的可迭代对象,能够以批量的形式加载数据,控制批量大小、元素的采样方法,并将批量结果整理成模型所需的输入形式。此外,DataLoader支持多进程读取数据,提升数据加载效率。

       构建数据管道通常只需要实现Dataset的__len__方法和__getitem__方法。对于复杂的数据集,可能还需要自定义DataLoader中的collate_fn函数来处理批量数据。

       深入理解Dataset和DataLoader的原理有助于你构建更加高效的数据管道。获取一个批量数据的步骤包括确定数据集长度、抽样出指定数量的元素、根据元素下标获取数据集中的元素,以及整理结果为两个张量。在这一过程中,数据集的长度由Dataset的__len__方法确定,元素的抽样方法由DataLoader的sampler和batch_sampler参数控制,元素获取逻辑在Dataset的__getitem__方法中实现,批量结果整理则由DataLoader的collate_fn函数完成。

       Dataset和DataLoader的源码提供了灵活的控制和优化机制,如调整batch大小、控制数据加载顺序、新荣耀程序源码选择采样方法等。以下是一些常用的Dataset和DataLoader功能的实现方式:

       使用Dataset创建数据集的方法有多种,包括基于Tensor创建数据集、根据目录创建数据集以及创建自定义数据集等。通过继承torch.utils.data.Dataset类,你可以轻松地创建自定义数据集。

       DataLoader的函数签名较为简洁,主要参数包括dataset、batch_size、shuffle、num_workers、pin_memory和drop_last等。在构建数据管道时,只需合理配置这些参数即可。对于复杂结构的数据集,可能还需要自定义collate_fn函数来处理批量数据的特殊需求。

       总的来说,通过深入理解Dataset和DataLoader的原理,你可以更高效地构建数据管道,优化数据加载流程,从而提升机器学习项目的训练效率和性能。无论是处理简单的数据集还是复杂的数据结构,遵循上述原则和方法,你都能够构建出高效且易于维护的数据管道。

retinanet 网络详解

       主干网络采用ResNet作为backbone。

       FPN层:输入照片尺寸为x,经过池化层后,通过ResNet网络提取特征,得到四个不同尺度的特征图,分别为layer1, layer2, layer3, layer4。源代码中的尺度融合从layer2层开始,经过尺度融合后得到f3, f4, f5, f6, f7五个不同尺度的特征层。

       一、Focal Loss:Retinanet网络的核心是Focal Loss,它在精度上超越了two-stage网络的精度,在速度上超越了one-stage网络的速度,首次实现了对二阶段网络的全面超越。

       Focal Loss是在二分类交叉熵的基础上进行修改,首先回顾一下二分类交叉熵损失。在训练过程中,正样本所占的损失权重较大,负样本所占的损失权重较小。然而,由于负样本的数量较多,即使权重较小,但大量样本数量叠加后同样带来很大的损失,导致在训练迭代过程中难以优化到最优状态。因此,Focal Loss在此基础上进行了改进。

       Focal Loss损失:论文中指出gamma=2.0, alpha=0.。当预测样本为简单正样本时,假设p=0.9,(1-p)的gamma次方会变得很小,因此损失函数值会变得非常小。对于负样本而言,当预测概率为0.5时,损失只减少0.倍,因此损失函数更加关注这类难以区分的样本。

       二、源代码讲解:model.py、anchors.py、losses.py、dataloader.py、train.py以上部分均为个人理解,如有错误欢迎各位批评指正。

       目前已实现口罩数据集检测,效果如下:

PyTorch - DataLoader 源码解析(一)

       本文为作者基于个人经验进行的初步解析,由于能力有限,可能存在遗漏或错误,敬请各位批评指正。

       本文并未全面解析 DataLoader 的全部源码,仅对 DataLoader 与 Sampler 之间的联系进行了分析。以下内容均基于单线程迭代器代码展开,多线程情况将在后续文章中阐述。

       以一个简单的数据集遍历代码为例,在循环中,数据是如何从 loader 中被取出的?通过断点调试,我们发现循环时,代码进入了 torch.utils.data.DataLoader 类的 __iter__() 方法,具体内容如下:

       可以看到,该函数返回了一个迭代器,主要由 self._get_iterator() 和 self._iterator._reset(self) 提供。接下来,我们进入 self._get_iterator() 方法查看迭代器的产生过程。

       在此方法中,根据 self.num_workers 的数量返回了不同的迭代器,主要区别在于多线程处理方式不同,但这两种迭代器都是继承自 _BaseDataLoaderIter 类。这里我们先看单线程下的例子,进入 _SingleProcessDataLoaderIter(self)。

       构造函数并不复杂,在父类的构造器中执行了大量初始化属性,然后在自己的构造器中获得了一个 self._dataset_fetcher。此时继续单步前进断点,发现程序进入到了父类的 __next__() 方法中。

       在分析代码之前,我们先整理一下目前得到的信息:

       下面是 __next__() 方法的内容:

       可以看到最后返回的是变量 data,而 data 是由 self._next_data() 生成的,进入这个方法,我们发现这个方法由子类负责实现。

       在这个方法中,我们可以看到数据从 self._dataset_fecther.fetch() 中得到,需要依赖参数 index,而这个 index 由 self._next_index() 提供。进入这个方法可以发现它是由父类实现的。

       而前面的 index 实际上是由这个 self._sampler_iter 迭代器提供的。查找 self._sampler_iter 的定义,我们发现其在构造函数中。

       仔细观察,我们可以在倒数第 4 行发现 self._sampler_iter = iter(self._index_sampler),这个迭代器就是这里的 self._index_sampler 提供的,而 self._index_sampler 来自 loader._index_sampler。这个 loader 就是最外层的 DataLoader。因此我们回到 DataLoader 类中查看这个 _index_sampler 是如何得到的。

       我们可以发现 _index_sampler 是一个由 @property 装饰得到的属性,会根据 self._auto_collation 来返回 self.batch_sampler 或者 self.sampler。再次整理已知信息,我们可以得到:

       因此,只要知道 batch_sampler 和 sampler 如何返回 index,就能了解整个流程。

       首先发现这两个属性来自 DataLoader 的构造函数,因此下面先分析构造函数。

       由于构造函数代码量较大,因此这里只关注与 Sampler 相关的部分,代码如下:

       在这里我们只关注以下部分:

       代码首先检查了参数的合法性,然后进行了一轮初始化属性,接着判断了 dataset 的类型,处理完特殊情况。接下来,函数对参数冲突进行了判断,共判断了 3 种参数冲突:

       检查完参数冲突后,函数开始创建 sampler 和 batch_sampler,如下图所示:

       注意,仅当未指定 sampler 时才会创建 sampler;同理,仅在未指定 batch_sampler 且存在 batch_size 时才会创建 batch_sampler。

       在 DataLoader 的构造函数中,如果不指定参数 batch_sampler,则默认创建 BatchSampler 对象。该对象需要一个 Sampler 对象作为参数参与构造。这也是在构造函数中,batch_sampler 与 sampler 冲突的原因之一。因为传入一个 batch_sampler 时,说明 sampler 已经作为参数完成了 batch_sampler 的构造,若再将 sampler 传入 DataLoader 是多余的。

       以第一节中的简单代码为例,此时并未指定 Sampler 和 batch_sampler,也未指定 batch_size,默认为 1,因此在 DataLoader 构造时,创建了一个 SequencialSampler,并传入了 BatchSampler 进行构建。继续第一节中的断点,可以发现:

       具体使用 sampler 还是 batch_sampler 来生成 index,取决于 _auto_collation,而从上面的代码发现,只要存在 self.batch_sampler 就永远使用 batch_sampler 来生成。batch_sampler 与 sampler 冲突的原因之二:若不设置冲突,那么使用者试图同时指定 batch_sampler 与 sampler 后,尤其是在使用者继承了新的 Sampler 子类后, sampler 在获取数据的时候完全没有被使用,这对开发者来说是一个困惑的现象,容易引起不易察觉的 BUG。

       继续断点发现程序进入了 BatchSampler 的 __iter__() 方法,代码如下:

       从代码中可以发现,程序不停地从 self.sampler 中获取 idx 加入列表,直到填满一个 batch 的量,并将这一整个 batch 的 index 返回到迭代器的 _next_data()。

       此处由 self._dataset_fetcher.fetch(index) 来获取真正的数据,进入函数后看到:

       这里依然根据 self.auto_collation(来自 DataLoader._auto_collation)进行分别处理,但是总体逻辑都是通过 self.dataset[] 来调用 Dataset 对象的 __getitem__() 方法。

       此处的 Dataset 是来自 torchvision 的 DatasetFolder 对象,这里读取文件路径中的后,经过转换变为 Tensor 对象,与标签 target 一起返回。参数中的 index 是由迭代器的 self._dataset_fetcher.fetch() 传入。

       整个获取数据的流程可以用以下流程图简略表示:

       注意:

       另附:

       对于一条循环语句,在执行过程中发生了以下事件:

MMDetection3D之DETR3D源码解析:整体流程篇

       关于torch.distributed.launch的更多细节: blog.csdn.net/magic_ll/...

       设置config file和work dir,work dir保存最终config,log等信息,work dir默认为path/to/user/work_dir/

       作者将自定义的部分放在 'projects/mmdet3d_plugin/' 文件夹下,通过registry类注册模块,这里利用importlib导入模块并初始化自定义的类。

       这里设置模型的输出信息保存路径、gpus等模型的运行时环境参数

       这里初始化模型,初始化train_dataset和val_dataset

       这部分完成了DataLoader的初始化,runner和hooks的初始化,并且按照workflow运行runner。

详解数据读取--Dataset, Samper, Dataloader

       在使用Pytorch进行模型训练时,数据读取过程常涉及到Dataset、Dataloader以及Sampler三个核心组件。通常情况下,我们自定义一个继承自Dataset的类来创建数据集,并作为Dataloader的初始化参数。Dataloader则根据初始化参数如batch_size和shuffle等完成数据加载。本文将深入解析这三个组件如何协同作用,完成数据读取任务。

       在构建Dataloader时,两个关键参数sampler和batch_sampler及collate_fn通常被指定。sampler需要继承自torch.utils.data.Sampler类,而collate_fn通常是一个函数。未指定时它们具有默认值。数据读取流程是由Dataset、Dataloader和Sampler共同完成的。本文章将通过源码解析它们如何协同工作。

       在理解Dataset、Dataloader和Sampler的联动之前,我们先对迭代器和生成器的概念进行梳理。迭代器iterator和可迭代对象iterable是Python中用于数据遍历的基础概念。一个iterable对象能够通过`iter()`函数获取其对应的iterator对象,而iterator对象在遍历时通过`next()`函数获取iterable中的下一个元素。实际上,for循环的`in`操作符在背后依赖于iterable和iterator的相互作用。

       生成器generator是一种特殊的迭代器,具有`yield`关键字,可以实现函数的暂停与恢复,非常适合用于生成序列数据。其操作方式类似于函数调用,但能暂停执行并在需要时恢复,生成序列数据。

       在数据读取流程中,Dataloader创建的迭代器最终指向Dataset。具体实现中,Dataloader首先初始化一个iterator对象,通常基于自定义的Sampler。当使用for循环遍历Dataloader时,实际上在遍历这个迭代器。Sampler负责确定数据读取顺序,而Dataset提供实际的数据点。Dataloader内部实现了一个`_next_data()`函数,负责从Dataset中提取并打包成批次数据,再通过`collate_fn`处理,最终生成训练批次。

       在Dataloader中,`_next_index()`函数用于获取下一个批次的索引。这些索引由Sampler生成,通常基于随机或顺序策略。获取索引后,Dataloader使用`_dataset_fetcher.fetch(index)`从Dataset中读取数据点。Dataset可能根据其类型(如`IterableDataset`或继承自`Dataset`的自定义类)实现具体的读取逻辑,通常通过`__getitem__`方法获取指定索引的数据。

       最后,数据点通过`collate_fn`进行打包,确保批次中的数据结构一致,适应模型训练的需求。整个过程展示了Dataset、Dataloader和Sampler如何协同工作,从数据集读取数据点,确定读取顺序,到最终生成可用于模型训练的批次数据。

       综上所述,理解Dataset、Dataloader和Sampler的协同作用是构建高效数据加载系统的关键。通过精心设计这些组件,可以显著提高数据处理效率,优化模型训练过程。

MindSpore从Pytorch迁移至MindSpore——数据处理(实战篇)

       在转换至MindSpore的数据处理领域,我们首先回顾了从Pytorch迁移至MindSpore的基本概念与方法。这里以Yolov5源码作为实例,深入解析数据集导入和处理的迁移过程,展示MindSpore与Pytorch在数据加载方面的差异与优劣。

       在Yolov5的Pytorch源码中,数据集导入主要依赖于`create_dataloader`函数,其中包含了数据集的创建和数据加载器的创建。重点观察了`LoadImagesAndLabels`类的实现,该类负责图像的导入、预处理以及数据集的相关操作。其中,`__getitem__`函数对图像进行各种变换,包括但不限于mosaic、mixup等,而`albumentations`库的使用体现了其在图像处理效率和全面性方面的优势。对于这些函数,直接使用即可,无需迁移。

       在MindSpore中,我们采用了一种更加灵活和高效的数据处理策略。为了简化参数管理,直接在初始化函数中设置超参数,并通过字典管理图像变换方法,显著提高了代码的可读性。针对`len`函数和`getitem`函数的实现,我们明确指定了哪些列在获取单个样本时返回,以及在组合成批次时应返回的列,进一步优化了数据处理流程。

       尽管迁移过程在模型导入、流程控制等方面仍有待深入探索,但通过上述实例,我们能够清晰地看到MindSpore在数据处理方面的独特优势和灵活性。与Pytorch相比,MindSpore提供了更为高效的数据加载和处理机制,这有助于提升模型训练的速度和性能。

       迁移过程中,我们关注于关键数据处理模块的改进与优化,以确保模型在MindSpore环境下的高效运行。通过比较Pytorch与MindSpore在数据集导入、处理方面的差异,我们可以发现,MindSpore在实现类似Yolov5算法时,不仅提供了与原生环境相媲美的功能,还通过优化数据处理流程,进一步增强了模型的训练效率。

       总结而言,从Pytorch迁移到MindSpore的数据处理过程中,关键在于理解两者在数据集导入和处理机制上的异同,并据此调整代码,以充分利用MindSpore在数据处理方面的优势。这一过程不仅为Yolov5算法的迁移提供了实际的指导,也为其他模型在MindSpore环境下的实现提供了参考与借鉴。

本文地址:http://581.net.cn/news/21d376196217.html 欢迎转发