【Pytorch】データ読み取りの詳細説明

コードを勉強してから追加します

データベースデータベース+データセット+サンプラー=ローダー

from torch.utils.data import *

IMDB +  + || =Dataset  Sampler  BatchSampler  DataLoader

1データベース

Image DataBaseはIMDBと略され、ファイルに保存されているデータ情報を参照します。

ファイル形式は変更できます。xml、yaml、json、sqlなど。

VOCはxml形式、COCOはJSON形式です。

IMDBを構築するプロセスは、これらのファイルを解析してデータインデックスを確立するプロセスです。

通常、後続の反復で読みやすいようにPythonリストとして解析されます。

2データセットDataSet

データセット:データベースIMDBに基づいて、データへのシングルトンまたはスライスアクセス方法を提供します。

つまり、データベース内のオブジェクトのインデックス作成メカニズムと、シングルトンインデックスまたはスライスインデックスの実装方法を定義することです。

つまり、DataSetは__getitem__、データセットを定義することにより、インデックス可能なオブジェクト、つまりインデックス可能なオブジェクトです。

つまり、特定のインデックスインデックスを渡した後、このインデックス、シングルトン、またはスライスに従ってシングルトンまたはスライスアクセスを実行する方法は、インデックスが単一の値であるかリストであるかによって異なります。

Pytorchのソースコードは次のとおりです。

class Dataset(object):
    """An abstract class representing a Dataset.
    All other datasets should subclass it. All subclasses should override
    ``__len__``, that provides the size of the dataset, and ``__getitem__``,
    supporting integer indexing in range from 0 to len(self) exclusive.
    """
    # 定义单例/切片访问方法,即 dataItem = Dataset[index]
    def __getitem__(self, index):
        raise NotImplementedError
    def __len__(self):
        raise NotImplementedError
    def __add__(self, other):
        return ConcatDataset([self, other])

前述のデータセット基本クラスとIMDB基本クラスに基づいてデータセットをカスタマイズする方法は2つあります。

# 方法一: 单继承
class XxDataset(Dataset)
    # 将IMDB作为参数传入,进行二次封装
    imdb = IMDB()
    pass
# 方法二: 双继承
class XxDataset(IMDB, Dataset):
    pass

3サンプラー&バッチサンプラー

実際のアプリケーションでは、データは必ずしも通常の順序でアクセスされるとは限りませんが、ランダムな順序でランダムにアクセスする必要があります。そうでない場合は、ランダムな加重アクセスが必要です。

したがって、特定のルールに従ってデータを読み取ることはサンプリング操作であり、サンプラーを定義する必要があります:サンプラー

また、データを1つずつ読み取ることはできませんが、バッチで読み取る必要があります。つまり、バッチサンプリング操作が必要です。バッチサンプラーを定義しますBatchSampler

したがって、データセットのシングルトンアクセス方式だけでは不十分です。これに基づいて、バッチアクセス方式をさらに定義する必要があります。

つまり、サンプラーはインデックス生成ルールを定義し、指定されたルールに従ってインデックスを生成することで、データ読み取りメカニズムを制御します。

BatchSamplerは、サンプラーに基づいて構築されます:  BatchSampler = Sampler + BatchSize

Pytorchのソースコードは次のとおりです。

 
class Sampler(object):
    """Base class for all Samplers.
    采样器基类,可以基于此自定义采样器。
    Every Sampler subclass has to provide an __iter__ method, providing a way
    to iterate over indices of dataset elements, and a __len__ method that
    returns the length of the returned iterators.
    """
    def __init__(self, data_source):
        pass
    def __iter__(self):
        raise NotImplementedError
    def __len__(self):
        raise NotImplementedError
# 序惯采样
class SequentialSampler(Sampler):
    def __init__(self, data_source):
        self.data_source = data_source
    def __iter__(self):
        return iter(range(len(self.data_source)))
    def __len__(self):
        return len(self.data_source)
# 随机采样
class RandomSampler(Sampler):
    def __init__(self, data_source):
        self.data_source = data_source
    def __iter__(self):
        return iter(torch.randperm(len(self.data_source)).long())
    def __len__(self):
        return len(self.data_source)
# 随机子采样
class SubsetRandomSampler(Sampler):
    pass
# 加权随机采样
class WeightedRandomSampler(Sampler):
    pass

 

 
class BatchSampler(object):
    """Wraps another sampler to yield a mini-batch of indices.
    Args:
        sampler (Sampler): Base sampler.
        batch_size (int): Size of mini-batch.
        drop_last (bool): If ``True``, the sampler will drop the last batch if
            its size would be less than ``batch_size``
    Example:
        >>> list(BatchSampler(range(10), batch_size=3, drop_last=False))
        [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
        >>> list(BatchSampler(range(10), batch_size=3, drop_last=True))
        [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
    """
    def __init__(self, sampler, batch_size, drop_last):
        self.sampler = sampler  # ******
        self.batch_size = batch_size
        self.drop_last = drop_last
    def __iter__(self):
        batch = []
        for idx in self.sampler:
            batch.append(idx)
            if len(batch) == self.batch_size:
                yield batch
                batch = []
        if len(batch) > 0 and not self.drop_last:
            yield batch
    def __len__(self):
        if self.drop_last:
            return len(self.sampler) // self.batch_size
        else:
            return (len(self.sampler) + self.batch_size - 1) // self.batch_size

 

上記から、Samplerは基本的に特定のルールを持つ反復可能なオブジェクトであることがわかりますが、反復できるのは1つのインスタンスのみです。

たとえば [x for x in range(10)]、range(10)は最も基本的なサンプラーであり、各ループで取得できる値は1つだけです。

[x for x in range(10)]
Out[10]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
from torch.utils.data.sampler import SequentialSampler
[x for x in SequentialSampler(range(10))]
Out[14]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
from torch.utils.data.sampler import RandomSampler
[x for x in RandomSampler(range(10))]
Out[12]: [4, 9, 5, 0, 2, 8, 3, 1, 7, 6]

 

BatchSamplerは、サンプラーを2回カプセル化し、batchSizeパラメーターを導入してバッチ反復を実現します。

from torch.utils.data.sampler import BatchSampler
[x for x in BatchSampler(range(10), batch_size=3, drop_last=False)]
Out[9]: [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
[x for x in BatchSampler(RandomSampler(range(10)), batch_size=3, drop_last=False)]
Out[15]: [[1, 3, 7], [9, 2, 0], [5, 4, 6], [8]]

4ローダーDataLoader

実際の計算では、メモリの制限とIO速度の低下を考慮して、データ量が多い場合、

したがって、一度にメモリにロードしたり、1つのスレッドだけでロードしたりすることはできません。

したがって、マルチスレッドと反復ロードが必要であるため、ローダーは具体的に定義されています:DataLoader

DataLoaderは反復可能オブジェクトである反復可能オブジェクトであり、内部——iter——マジック関数が構成されており、それを呼び出すと反復子が返されます。

この関数はiter組み込み関数、つまりによって直接呼び出すことできます DataIteror = iter(DataLoader)

dataloader = DataLoader(dataset=Dataset(imdb=IMDB()), sampler=Sampler(), num_works, ...)

__init__パラメータは2つの部分で構成され、前半はを指定するために使用され数据集 + 采样器後半はを指定するために使用されます多线程参数

class DataLoader(object):
    """
    Data loader. Combines a dataset and a sampler, and provides
    single- or multi-process iterators over the dataset.
    """
    def __init__(self, dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None,
                 num_workers=0, collate_fn=default_collate, pin_memory=False, drop_last=False,
                 timeout=0, worker_init_fn=None):
        self.dataset = dataset
        self.batch_size = batch_size
        self.num_workers = num_workers
        self.collate_fn = collate_fn
        self.pin_memory = pin_memory
        self.drop_last = drop_last
        self.timeout = timeout
        self.worker_init_fn = worker_init_fn
        if timeout < 0:
            raise ValueError('timeout option should be non-negative')
        # 检测是否存在参数冲突: 默认batchSampler vs 自定义BatchSampler
        if batch_sampler is not None:
            if batch_size > 1 or shuffle or sampler is not None or drop_last:
                raise ValueError('batch_sampler is mutually exclusive with '
                                 'batch_size, shuffle, sampler, and drop_last')
        if sampler is not None and shuffle:
            raise ValueError('sampler is mutually exclusive with shuffle')
        if self.num_workers < 0:
            raise ValueError('num_workers cannot be negative; '
                             'use num_workers=0 to disable multiprocessing.')
        # 在此处会强行指定一个 BatchSampler
        if batch_sampler is None:
            # 在此处会强行指定一个 Sampler
            if sampler is None:
                if shuffle:
                    sampler = RandomSampler(dataset)
                else:
                    sampler = SequentialSampler(dataset)
            batch_sampler = BatchSampler(sampler, batch_size, drop_last)
        # 使用自定义的采样器和批采样器
        self.sampler = sampler
        self.batch_sampler = batch_sampler
    def __iter__(self):
        # 调用Pytorch的多线程迭代器加载数据
        return DataLoaderIter(self)
    def __len__(self):
        return len(self.batch_sampler)

 

5データイテレータDataLoaderIter

イテレータとイテレータオブジェクトには違いがあります。

反復可能オブジェクトとは、Iter関数がそのオブジェクト使用されるときに、イテレータを返すことができるため、継続的に反復的にアクセスできることを意味します。

イテレータオブジェクトには、内部に追加のマジック関数があり__next__ます。組み込み関数を使用して操作するnextと、次のデータを継続的に生成できます。生成ルールは、この関数によって決定されます。

反復可能オブジェクトは、オブジェクトが反復可能であることを示しますが、特定の反復ルールはイテレーターによって記述されます。このデカップリングの利点は、同じ反復可能オブジェクトを、異なるルールを持つ複数のイテレーターで構成できることです。

 

6データセット/コンテナトラバーサルの一般化されたプロセス:NILIS

NILIS规则:data = next(iter(loader(DataSet [sampler])))data = nextiterloaderDataSet [ sampler ])))

  1. サンプラー は、インデックスの生成ルールを定義し、インデックスリストを返し、後続のインデックスアクセスプロセスを制御します。
  2. __item__インデックスコンテナと、インデックス可能なオブジェクトとして使用できるコンテナによって定義されたルールに 基づくインデクサー[]操作。
  3. ローダーは__iter__、コンテナーの反復可能性の定義に 基づいおり、コンテナーをiter()で操作できる反復可能オブジェクトにするためにイテレーターを返すなど、ロード規則を記述します
  4. 次にされた ベース__next__イテレータを定義する上、容器の特定の反復ルールを記述し、そして容器を製造するイテレータオブジェクト(次によって操作することができます)。
## 初始化
sampler = Sampler()
dataSet = DataSet(sampler)            # __getitem__
dataLoader = DataLoader(dataSet, sampler) / DataIterable()        # __iter__()
dataIterator = DataLoaderIter(dataLoader)     #__next__()
data_iter = iter(dataLoader)
## 遍历方法1
for _ in range(len(data_iter))
    data = next(data_iter)
## 遍历方法2
for i, data in enumerate(dataLoader):
    data = data

 

おすすめ

転載: blog.csdn.net/u013066730/article/details/114288514