Subclass of `DataLoader` using a `ThreadBuffer` object to implement `__iter__` method asynchronously. This will iterate over data from the loader as expected however the data is generated on a separate thread. Use this class where a `DataLoader` instance is required and not just an iter
| 135 | |
| 136 | |
| 137 | class ThreadDataLoader(DataLoader): |
| 138 | """ |
| 139 | Subclass of `DataLoader` using a `ThreadBuffer` object to implement `__iter__` method asynchronously. This will |
| 140 | iterate over data from the loader as expected however the data is generated on a separate thread. Use this class |
| 141 | where a `DataLoader` instance is required and not just an iterable object. |
| 142 | |
| 143 | The default behaviour with `repeats` set to 1 is to yield each batch as it is generated, however with a higher |
| 144 | value the generated batch is yielded that many times while underlying dataset asynchronously generates the next. |
| 145 | Typically not all relevant information is learned from a batch in a single iteration so training multiple times |
| 146 | on the same batch will still produce good training with minimal short-term overfitting while allowing a slow batch |
| 147 | generation process more time to produce a result. This duplication is done by simply yielding the same object many |
| 148 | times and not by regenerating the data. |
| 149 | |
| 150 | Another typical usage is to accelerate light-weight preprocessing (usually cached all the deterministic transforms |
| 151 | and no IO operations), because it leverages the separate thread to execute preprocessing to avoid unnecessary IPC |
| 152 | between multiple workers of DataLoader. And as CUDA may not work well with the multi-processing of DataLoader, |
| 153 | `ThreadDataLoader` can be useful for GPU transforms. For more details: |
| 154 | https://github.com/Project-MONAI/tutorials/blob/master/acceleration/fast_model_training_guide.md. |
| 155 | |
| 156 | The `use_thread_workers` will cause workers to be created as threads rather than processes although everything else |
| 157 | in terms of how the class works is unchanged. This allows multiple workers to be used in Windows for example, or in |
| 158 | any other situation where thread semantics is desired. Please note that some MONAI components like several datasets |
| 159 | and random transforms are not thread-safe and can't work as expected with `thread workers`, need to check all the |
| 160 | preprocessing components carefully before enabling `use_thread_workers`. |
| 161 | |
| 162 | See: |
| 163 | * Fischetti et al. "Faster SGD training by minibatch persistency." ArXiv (2018) https://arxiv.org/abs/1806.07353 |
| 164 | * Dami et al., "Faster Neural Network Training with Data Echoing" ArXiv (2020) https://arxiv.org/abs/1907.05550 |
| 165 | * Ramezani et al. "GCN meets GPU: Decoupling "When to Sample" from "How to Sample"." NeurIPS (2020). |
| 166 | https://proceedings.neurips.cc/paper/2020/file/d714d2c5a796d5814c565d78dd16188d-Paper.pdf |
| 167 | |
| 168 | Args: |
| 169 | dataset: input dataset. |
| 170 | buffer_size: number of items to buffer from the data source. |
| 171 | buffer_timeout: time to wait for an item from the buffer, or to wait while the buffer is full when adding items. |
| 172 | repeats: number of times to yield the same batch. |
| 173 | use_thread_workers: if True and num_workers > 0 the workers are created as threads instead of processes |
| 174 | kwargs: other arguments for `DataLoader` except for `dataset`. |
| 175 | |
| 176 | """ |
| 177 | |
| 178 | def __init__( |
| 179 | self, |
| 180 | dataset: Dataset, |
| 181 | buffer_size: int = 1, |
| 182 | buffer_timeout: float = 0.01, |
| 183 | repeats: int = 1, |
| 184 | use_thread_workers: bool = False, |
| 185 | **kwargs, |
| 186 | ): |
| 187 | # if workers should be threads, create a new multiprocessing context with the process and queue types |
| 188 | # substituted with the shim types given above |
| 189 | if use_thread_workers and kwargs.get("num_workers", 0) > 0: |
| 190 | kwargs["multiprocessing_context"] = _ProcessThreadContext() |
| 191 | kwargs["persistent_workers"] = False |
| 192 | |
| 193 | super().__init__(dataset, **kwargs) |
| 194 | self.buffer_size = buffer_size |
no outgoing calls
searching dependent graphs…