NVIDIA / NVIDIA/DALI

webdataset cannot stop cycling at end of epoch

Open
#5,441 20 comments 0 reactions 1 assignee View on GitHub

@stiepan is already working on this.

Since Apr 22, 2024.

enhancement
Dominant language
C++
Stars
5.8k
Forks
678
Avg merge
3d 1h
Merged PRs (30d)
27

Description

Version

1.31.0

Describe the bug.

I used dataset of about 2w samples, and the iteration of data should stop at iteration of 700. However, the dataloader would continue feed dataset batches after than, and the training will not stop.

Minimum reproducible example
Here is a piece of my code, which is the main part of dataloader:


import os.path as osp
import re
import time
import random

import numpy as np

from nvidia.dali.plugin.pytorch import DALIClassificationIterator, LastBatchPolicy, DALIGenericIterator
from nvidia.dali.pipeline import pipeline_def
import nvidia.dali.types as types
import nvidia.dali.fn as fn




@pipeline_def
def create_dali_pipeline_segment(wds_paths, shard_id, num_shards, dali_cpu=False,
                                 scales=[0.75, 2], cropsize=[1024, 1024],
                                 mean=[0.3257, 0.3690, 0.3223],
                                 std=[0.2112, 0.2148, 0.2115],
                                 ):

    wds_index_paths = [re.sub('tar$', 'idx', el) for el in wds_paths]
    images = fn.readers.webdataset(
        paths=wds_paths,
        index_paths=wds_index_paths,
        ext=['jpg',], missing_component_behavior="error",
        dtypes=[types.UINT8, ],
        random_shuffle=True,
        pad_last_batch=False,
        prefetch_queue_depth=4,
        shard_id=shard_id,
        num_shards=num_shards,
        read_ahead=True,
        device='cpu'
    )


    dali_device = 'cpu' if dali_cpu else 'gpu'
    decoder_device = 'cpu' if dali_cpu else 'mixed'
    # ask nvJPEG to preallocate memory for the biggest sample in ImageNet for CPU and GPU to avoid reallocations in runtime
    device_memory_padding = 211025920 if decoder_device == 'mixed' else 0
    host_memory_padding = 140544512 if decoder_device == 'mixed' else 0
    # ask HW NVJPEG to allocate memory ahead for the biggest image in the data set to avoid reallocations in runtime
    preallocate_width_hint = 5980 if decoder_device == 'mixed' else 0
    preallocate_height_hint = 6430 if decoder_device == 'mixed' else 0



    ## decode and switch to gpu
    shape = fn.peek_image_shape(images)
    images = fn.decoders.image(images, device='mixed', output_type=types.RGB)
    #  shape = fn.shapes(images)
    images = images.gpu()

    # random resize
    scale = fn.random.uniform(range=(min(scales), max(scales)))
    new_size = shape[0:2] * scale
    images = fn.resize(images, size=new_size,
                       interp_type=types.DALIInterpType.INTERP_LINEAR, antialias=False)

    # random crop
    crop_pos_x = fn.random.uniform(range=(0, 1))
    crop_pos_y = fn.random.uniform(range=(0, 1))
    images = fn.crop(images, crop=cropsize, crop_pos_x=crop_pos_x, crop_pos_y=crop_pos_y, out_of_bounds_policy="pad", fill_values=0)


    images = fn.transpose(images, perm=[2, 0, 1])
    images = fn.normalize(
        images,
        dtype=types.FLOAT,
        mean=255 * np.array(mean).reshape(-1, 1, 1),
        stddev=255 * np.array(std).reshape(-1, 1, 1))


    return images,


class OneEpochWraper(object):

    def __init__(self, dl, n_epochs):
        self.dl = iter(dl)
        self.n_epochs = n_epochs
        self.epoch = 0
        self.it = 0

    def __iter__(self):
        self.epoch += 1
        return self

    def __next__(self):
        print('iter: ', self.it)
        self.it += 1
        try:
            return next(self.dl)
        except StopIteration:
            print('epoch done: ', self.epoch)
            #  self.dl = iter(dl)
            #  if self.epoch >= self.n_epochs:
            #      raise StopIteration
            #  return next(self.dl)



def create_dali_loader(cfg, mode='train'):

    rank = int(os.environ['RANK'])
    local_rank = int(os.environ['LOCAL_RANK'])
    world_size = int(os.environ['WORLD_SIZE'])
    local_world_size = int(os.environ['LOCAL_WORLD_SIZE'])

    im_root = cfg.im_root
    im_anno = cfg.train_im_anns

    batchsize = cfg.global_batchsize #// world_size
    n_epochs = cfg.n_epochs

    dali_num_threads = 8

    saroot = '../../../datasets_share_to_all/SA-1B/raw/'
    wds_paths = [
        osp.join(saroot, 'sa_000198.coin.tar'),
        osp.join(saroot, 'sa_000199.coin.tar'),
    ]

    pipe = create_dali_pipeline_segment(batch_size=batchsize,
                                wds_paths=wds_paths,
                                num_threads=dali_num_threads,
                                device_id=local_rank,
                                seed=12 + local_rank,
                                dali_cpu=False,

                                prefetch_queue_depth=16,
                                shard_id=local_rank,
                                num_shards=world_size,
                                **cfg.dali_pipe_kwargs,
                                         )
    pipe.build()
    data_loader = DALIGenericIterator(pipe,
                                      ['data_0', ],
                                      last_batch_policy=LastBatchPolicy.DROP,
                                      auto_reset=False)

    n_samples = pipe.epoch_size()['__Webdataset_0']
    n_iters = (n_samples // cfg.global_batchsize) * cfg.n_epochs ## this is 700

    #  data_iter = OneEpochWraper(data_loader, cfg.n_epochs)
    data_iter = data_loader

    return data_iter, n_iters

dl, _ = create_dali_loader(...)

for it, data in enumerate(dl):
    print(it)  # this will not stop at 700, even not until 1000+


### Relevant log output

_No response_

### Other/Misc.

_No response_

### Check for duplicates

- [X] I have searched the [open bugs/issues](https://github.com/NVIDIA/DALI/issues) and have found no duplicates for this bug report
```[tasklist]
### Tasks

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.