YOLO dataloader

不理解 看代码 看代码 看代码 重要的事情说三遍 代码最好进行调试理解 代码是最好的老师

1 迭代器的简要介绍

1.1 iter和next的初步理解

__iter__(): class 中定义的魔术方法
__next__(): class 中定义的魔术方法
iter(): 将一个定义了__iter__()的类转化为一个迭代器
next(): 读取迭代器中的数据

iter()和next()的原理图可以参考这个链接:Pytorch(三):Dataset和Dataloader的理解

注意:当一个类中同时定义了__iter__()和__next__(),这个类本身就是一个迭代器,或者如果只定义了__iter__(),需要使用iter()将类对象转化为迭代器

next()有一个点需要注意(见下面代码):

  1. 类Exmaple同时定义了__iter__()__next__() => exmaple是一个迭代器,next(example)调用的是example中的__next__()
  2. 类Exmaple只定义了__iter__() => 需要用iter(example)将exmaple转化为一个迭代器,next(example)调用的是example中的__iter__()
class Example:
    def __init__(self, data):
        self.data = data
    
    def __iter__(self):
        for index in range(len(self.data)):
            print("打印iter")
            yield self.data[index]

    def __next__(self):
        print("打印next")

if __name__ == "__main__":
    example = Example(range(100))

    """
    不注释__next__()  Exmaple同时定义了__iter__()和__next__() => exmaple是一个迭代器
    当Example同时定义了__iter__()和__next__()的时候 => next(example)调用的是example中的__next__()
    """
    next(example)  

    """
    注释__next__()  Exmaple只定义了__iter__() => 需要用iter(example)将exmaple转化为一个迭代器
    当Example 只定义了 __iter__()的时候 => next(example)调用的是example中的__iter__()
    """
    # next(iter(example))  

for循环:当执行for i in obj:,Python解释器会在第一次迭代时自动调用iter(obj)生成一个迭代器Iter,然后在之后的迭代(包扩第一次迭代)会使用next()调用迭代器Iter的__iter__()方法,for语句会自动处理最后抛出的StopIteration异常,如list、tuple、dict等可迭代对象。但是当obj是一个类对象,且类中定义了__iter__()时,Python解释器会在循环开始时自动调用Iter = obj.__iter__()返回一个迭代器Iter,然后在之后的迭代(包括第一次迭代)会调用迭代器Iter的__next__()方法,for语句会自动处理最后抛出的StopIteration异常

如果obj是类似于list、tuple、dict的一个可迭代对象时,python解释器会自动为我们做以下几步操作(通义千问,重点不是for循环中这个用法,不用太过重视):

  • 调用 iter() 函数:首先,Python调用内置的iter()函数尝试从可迭代对象obj中获取一个迭代器。这相当于执行了iterator = iter(obj)。
  • 循环与 next() 方法:在for循环的每次迭代中,Python会使用next()调用迭代器的__iter__()方法来获取下一个元素。
  • 处理 StopIteration 异常:当迭代器没有更多的元素可以提供时,next()方法会抛出一个StopIteration异常。Python的for循环会捕获这个异常,并且知道这是迭代结束的信号,于是它会干净利落地结束循环,而不会将这个异常暴露给用户代码。
  • 具体的实现代码过程类似于
    for i in obj:
        # 循环体
    
    实际上,Python在幕后做的事情类似于下面的手动迭代过程:
    iterator = iter(obj)
    while True:
        try:
            i = next(iterator)
        except StopIteration:
            break  # 遇到StopIteration时退出循环
        # 循环体
    

1.2 torch.utils.data.DataLoader中的iter和next

DataLoader(torch.utils.data.DataLoader)重要的是:在数据集遍历的一般化过程中,执行for i, data in enumerate(dataLoader):,当dataLoader是一个类对象,且类中定义了__iter__()时,Python解释器会在循环开始时自动调用Iter = dataLoader.__iter__()返回一个迭代器Iter,然后在之后的迭代(包扩第一次迭代)会调用迭代器Iter的__next__()方法获取数据。

__iter__()的作用是使一个类对象成为可迭代的。在for循环中,Python调用iter(obj)时,如果类对象obj是可迭代的(即定义了__iter__),那么iter(obj)实际上等同于调用obj.__iter__()生成一个迭代器。 这个方法返回的迭代器随后被用于每次循环迭代中调用__next__()来获取下一个值,直到遇到StopIteration异常为止。

1.2.1 第一种方式

下面代码中,DataLoader的load_data方法通过yield from委托给了iter(self.sampler)的这个迭代器,从而直接迭代并输出数据。注意yeild from iter(self.sampler)中必须要使用iter(),因为self.sampler并不是一个迭代器(Sampler中只定义了__iter__()),需要使用iter(self.sampler)将self.sampler转换为迭代器。

# 打印0-99
class Sampler:
    def __init__(self, data):
        self.data = data
    
    def __iter__(self):
        for index in range(len(self.data)):
            yield self.data[index]

class DataLoader:
    def __init__(self, sampler):
        self.sampler = sampler
    
    def load_data(self):
        yield from iter(self.sampler)

# 使用示例
sampler = Sampler(range(100))  # 假设的采样器实例
data_loader = DataLoader(sampler)
for item in data_loader.load_data():
    print(item)

1.2.2 第二种方式,这种方式就类似于torch.utils.data.DataLoader

  1. for item in data_loader:中,会调用data_loader.__iter__()返回一个迭代器Iter,然后一直调用迭代器Iter的__next__()返回值。在for循环中的第一次循环调用data_loader.__iter__()生成一个迭代器Iter后,之后的循环就不再调用data_loader.__iter__()了,只调用迭代器Iter的__next__()读取数据。当然,如果再次使用for循环,它又会再次调用data_loader.__iter__()生成一个迭代器。如目标检测中,使用torch.utils.data.DataLoader读取数据,epochs=300时,每一个epoch,都会使用一次for循环,即都会调用一次__iter__()生成一个迭代器。
  2. 因为class DataLoader同时定义了__iter__()__next__(),所以DataLoader类本身就是一个迭代器,所以在其__iter__()中可以使用return self返回其本身这个迭代器Iter, 然后调用迭代器Iter的Iter.__next__()去读取数据。注意,这里不要理解错了,调用的__next__()是迭代器Iter的,如果class DataLoader中的__iter__()返回的不是self,而是其他迭代器如iter(self.sampler),那么就不会调用class DataLoader__next__(),而是调用iter(self.sampler)的__next__()
  3. 还有一个注意的地方,这个在torch.utils.data.DataLoader中也出现过,就是使用next(self.sampler)时一定要注意self.sampler必须是一个迭代器,因为sampler不是一个迭代器,所以需要在__init__()使用iter(sampler)将sampler转化成迭代器。
    # 打印0-99
    class Sampler:
        def __init__(self, data):
            self.data = data
        
        def __iter__(self):
            for index in range(len(self.data)):
                yield self.data[index]
            
    class DataLoader:
        def __init__(self, sampler):
            self.sampler = iter(sampler)
        
        def __iter__(self):
            return self
    
        def __next__(self):
            return next(self.sampler)
    
    # 使用示例
    sampler = Sampler(range(100))  # 假设的采样器实例
    data_loader = DataLoader(sampler)
    for item in data_loader:
        print(item)
    

1.2.3 第三种方式,这种方式就是torch.utils.data.DataLoader的实现方式

  1. for item in data_loader:中,会调用data_loader.__iter__()返回一个迭代器Iter,这个迭代器Iter就是DataLoaderIter(self)。self是DataLoader作为参数传入到DataLoaderIter进行初始化。注意:DataLoaderIter(self)中的__iter__()只是为了让DataLoaderIter成为一个迭代器,实际运行过程中并不调用这个__iter__()
  2. 在生成迭代器DataLoaderIter之后,会调用迭代器DataLoaderIter中的__next__()
    # 打印0-99
    # Sampler 类似于 BatchSampler
    class Sampler:
        def __init__(self, data):
            self.data = data
    
        def __iter__(self):
            for index in range(len(self.data)):
                yield self.data[index]
        
    # DataLoaderIter 类似于 _BaseDataLoaderIter
    class DataLoaderIter:
        def __init__(self, loader):
            self.sampler = iter(loader.sampler)
        
        def __iter__(self):
            return self
    
        def __next__(self):
            return next(self.sampler)
    
    # DataLoader 类似于 DataLoader
    class DataLoader:
        def __init__(self, sampler):
            self.sampler = sampler
        
        def __iter__(self):
            return DataLoaderIter(self)
    
    # 使用示例
    if __name__ == "__main__":
    	sampler = Sampler(range(100))  # 假设的采样器实例
    	data_loader = DataLoader(sampler)
    	epochs=3
    	for i in range(epochs):
    		for item in data_loader
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值