Contains a running `Engine` object within a separate thread from main thread in a Jupyter notebook. This allows an engine to begin a run in the background and allow the starting notebook cell to complete. A user can thus start a run and then navigate away from the notebook without conce
| 260 | |
| 261 | |
| 262 | class ThreadContainer(Thread): |
| 263 | """ |
| 264 | Contains a running `Engine` object within a separate thread from main thread in a Jupyter notebook. This |
| 265 | allows an engine to begin a run in the background and allow the starting notebook cell to complete. A |
| 266 | user can thus start a run and then navigate away from the notebook without concern for loosing connection |
| 267 | with the running cell. All output is acquired through methods which synchronize with the running engine |
| 268 | using an internal `lock` member, acquiring this lock allows the engine to be inspected while it's prevented |
| 269 | from starting the next iteration. |
| 270 | |
| 271 | Args: |
| 272 | engine: wrapped `Engine` object, when the container is started its `run` method is called |
| 273 | loss_transform: callable to convert an output dict into a single numeric value |
| 274 | metric_transform: callable to convert a named metric value into a single numeric value |
| 275 | status_format: format string for status key-value pairs. |
| 276 | """ |
| 277 | |
| 278 | def __init__( |
| 279 | self, |
| 280 | engine: Engine, |
| 281 | loss_transform: Callable = _get_loss_from_output, |
| 282 | metric_transform: Callable = lambda name, value: value, |
| 283 | status_format: str = "{}: {:.4}", |
| 284 | ): |
| 285 | super().__init__() |
| 286 | self.lock = RLock() |
| 287 | self.engine = engine |
| 288 | self._status_dict: dict[str, Any] = {} |
| 289 | self.loss_transform = loss_transform |
| 290 | self.metric_transform = metric_transform |
| 291 | self.fig: plt.Figure | None = None |
| 292 | self.status_format = status_format |
| 293 | |
| 294 | self.engine.add_event_handler(Events.ITERATION_COMPLETED, self._update_status) |
| 295 | |
| 296 | def run(self): |
| 297 | """Calls the `run` method of the wrapped engine.""" |
| 298 | self.engine.run() |
| 299 | |
| 300 | def stop(self): |
| 301 | """Stop the engine and join the thread.""" |
| 302 | self.engine.terminate() |
| 303 | self.join() |
| 304 | |
| 305 | def _update_status(self): |
| 306 | """Called as an event, updates the internal status dict at the end of iterations.""" |
| 307 | with self.lock: |
| 308 | state = self.engine.state |
| 309 | stats: dict[str, Any] = { |
| 310 | StatusMembers.EPOCHS.value: 0, |
| 311 | StatusMembers.ITERS.value: 0, |
| 312 | StatusMembers.LOSS.value: float("nan"), |
| 313 | } |
| 314 | |
| 315 | if state is not None: |
| 316 | if state.max_epochs is not None and state.max_epochs >= 1: |
| 317 | epoch = f"{state.epoch}/{state.max_epochs}" |
| 318 | else: |
| 319 | epoch = str(state.epoch) |
no outgoing calls
searching dependent graphs…