def METHOD_NAME(
    self,
    epoch: int,
    shuffle: bool = None,
) -> Iterator[Tuple[List[str], Dict[str, torch.Tensor]]]:
    per_sample_loader = self.per_sample_iter_factory.METHOD_NAME(epoch, shuffle)
    if shuffle is None:
        shuffle = self.shuffle
    state = np.random.RandomState(epoch + self.seed)
    # NOTE(kamo):
    #   This iterator supports multiple chunk lengths and
    #   keep chunks for each lengths here until collecting specified numbers
    cache_chunks_dict = {}
    cache_id_list_dict = {}
    for ids, batch in per_sample_loader:
        # Must be per-sample-loader
        assert len(ids) == 1, f"Must be per-sample-loader: {len(ids)}"
        assert all(len(x) == 1 for x in batch.values())
        # Get keys of sequence data
        sequence_keys = []
        for key in batch:
            if key + "_lengths" in batch:
                sequence_keys.append(key)
        # Remove lengths data and get the first sample
        batch = {k: v[0] for k, v in batch.items() if not k.endswith("_lengths")}
        id_ = ids[0]
        for key in sequence_keys:
            if len(batch[key]) != len(batch[sequence_keys[0]]):
                raise RuntimeError(
                    f"All sequences must has same length: "
                    f"{len(batch[key])} != {len(batch[sequence_keys[0]])}"
                )
        L = len(batch[sequence_keys[0]])
        # Select chunk length
        chunk_lengths = [lg for lg in self.chunk_lengths if lg < L]
        if len(chunk_lengths) == 0:
            logging.warning(
                f"The length of '{id_}' is {L}, but it is shorter than "
                f"any candidates of chunk-length: {self.chunk_lengths}"
            )
            continue
        W = int(state.choice(chunk_lengths, 1))
        cache_id_list = cache_id_list_dict.setdefault(W, [])
        cache_chunks = cache_chunks_dict.setdefault(W, {})
        # Shift width to the next chunk
        S = int(W * self.chunk_shift_ratio)
        # Number of chunks
        N = (L - W) // S + 1
        if shuffle:
            Z = state.randint(0, (L - W) % S + 1)
        else:
            Z = 0
        # Split a sequence into chunks.
        # Note that the marginal frames divided by chunk length are discarded
        for k, v in batch.items():
            if k not in cache_chunks:
                cache_chunks[k] = []
            if k in sequence_keys:
                # Shift chunks with overlapped length for data augmentation
                cache_chunks[k] += [v[Z + i * S : Z + i * S + W] for i in range(N)]
            else:
                # If not sequence, use whole data instead of chunk
                cache_chunks[k] += [v for _ in range(N)]
        cache_id_list += [id_ for _ in range(N)]
        if len(cache_id_list) > self.num_cache_chunks:
            cache_id_list, cache_chunks = yield from self._generate_mini_batches(
                cache_id_list,
                cache_chunks,
                shuffle,
                state,
            )
        cache_id_list_dict[W] = cache_id_list
        cache_chunks_dict[W] = cache_chunks
    else:
        for W in cache_id_list_dict:
            cache_id_list = cache_id_list_dict.setdefault(W, [])
            cache_chunks = cache_chunks_dict.setdefault(W, {})
            yield from self._generate_mini_batches(
                cache_id_list,
                cache_chunks,
                shuffle,
                state,
            )