{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "from local.test import *\n", "from local.basics import *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from local.notebook.showdoc import *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#default_exp callback.progress" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Progress and logging callbacks\n", "\n", "> Callback and helper function to track progress of training or log results" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from local.test_utils import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ProgressCallback -" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# export\n", "@docs\n", "class ProgressCallback(Callback):\n", " \"A `Callback` to handle the display of progress bars\"\n", " run_after=Recorder\n", "\n", " def begin_fit(self):\n", " assert hasattr(self.learn, 'recorder')\n", " self.mbar = master_bar(list(range(self.n_epoch)))\n", " self.mbar.on_iter_begin()\n", " if self.learn.logger != noop:\n", " self.old_logger,self.learn.logger = self.logger,self._write_stats\n", " self._write_stats(self.recorder.metric_names)\n", " else: self.old_logger = noop\n", "\n", " def begin_epoch(self): self.mbar.update(self.epoch)\n", " def begin_train(self): self._launch_pbar()\n", " def begin_validate(self): self._launch_pbar()\n", " def after_train(self): self.pbar.on_iter_end()\n", " def after_validate(self): self.pbar.on_iter_end()\n", " def after_batch(self):\n", " self.pbar.update(self.iter+1)\n", " if hasattr(self, 'smooth_loss'): self.pbar.comment = f'{self.smooth_loss:.4f}'\n", "\n", " def _launch_pbar(self):\n", " self.pbar = progress_bar(self.dl, parent=self.mbar)\n", " self.pbar.update(0)\n", "\n", " def after_fit(self):\n", " self.mbar.on_iter_end()\n", " self.learn.logger = self.old_logger\n", "\n", " def _write_stats(self, log):\n", " self.mbar.write([f'{l:.6f}' if isinstance(l, float) else str(l) for l in log], table=True)\n", "\n", " _docs = dict(begin_fit=\"Setup the master bar over the epochs\",\n", " begin_epoch=\"Update the master bar\",\n", " begin_train=\"Launch a progress bar over the training dataloader\",\n", " begin_validate=\"Launch a progress bar over the validation dataloader\",\n", " after_train=\"Close the progress bar over the training dataloader\",\n", " after_validate=\"Close the progress bar over the validation dataloader\",\n", " after_batch=\"Update the current progress bar\",\n", " after_fit=\"Close the master bar\")\n", "\n", "defaults.callbacks = [TrainEvalCallback, Recorder, ProgressCallback]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
epochtrain_lossvalid_losstime
014.75952612.15683000:00
111.5490068.65610300:00
27.8947005.73892000:00
35.1687613.62815400:00
43.2664192.23552300:00
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "learn = synth_learner()\n", "learn.fit(5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#hide\n", "assert not learn.progress.mbar.child.is_active\n", "lines = learn.progress.mbar.lines\n", "test_eq(learn.recorder.metric_names, lines[0])\n", "for i,(l,v) in enumerate(zip(lines[1:],learn.recorder.values)):\n", " test_eq(l[:-1], [str(i)] + [f'{x:.6f}' for x in v])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/html": [], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#hide\n", "#Check validate works without any training\n", "def tst_metric(out, targ): return F.mse_loss(out, targ)\n", "learn = synth_learner(n_trn=5, metrics=tst_metric)\n", "preds,targs = learn.validate()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/html": [], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#hide\n", "#Check get_preds works without any training\n", "learn = synth_learner(n_trn=5, metrics=tst_metric)\n", "preds,targs = learn.validate()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

ProgressCallback.begin_fit[source]

\n", "\n", "> ProgressCallback.begin_fit()\n", "\n", "Setup the master bar over the epochs" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(ProgressCallback.begin_fit)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

ProgressCallback.begin_epoch[source]

\n", "\n", "> ProgressCallback.begin_epoch()\n", "\n", "Update the master bar" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(ProgressCallback.begin_epoch)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

ProgressCallback.begin_train[source]

\n", "\n", "> ProgressCallback.begin_train()\n", "\n", "Launch a progress bar over the training dataloader" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(ProgressCallback.begin_train)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

ProgressCallback.begin_validate[source]

\n", "\n", "> ProgressCallback.begin_validate()\n", "\n", "Launch a progress bar over the validation dataloader" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(ProgressCallback.begin_validate)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

ProgressCallback.after_batch[source]

\n", "\n", "> ProgressCallback.after_batch()\n", "\n", "Update the current progress bar" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(ProgressCallback.after_batch)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

ProgressCallback.after_train[source]

\n", "\n", "> ProgressCallback.after_train()\n", "\n", "Close the progress bar over the training dataloader" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(ProgressCallback.after_train)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

ProgressCallback.after_validate[source]

\n", "\n", "> ProgressCallback.after_validate()\n", "\n", "Close the progress bar over the validation dataloader" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(ProgressCallback.after_validate)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

ProgressCallback.after_fit[source]

\n", "\n", "> ProgressCallback.after_fit()\n", "\n", "Close the master bar" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(ProgressCallback.after_fit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ShowGraphCallback -" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# export\n", "class ShowGraphCallback(Callback):\n", " \"Update a graph of training and validation loss\"\n", " run_after=ProgressCallback\n", "\n", " def begin_fit(self):\n", " self.nb_batches = []\n", " assert hasattr(self.learn, 'progress')\n", "\n", " def after_train(self): self.nb_batches.append(self.train_iter)\n", "\n", " def after_epoch(self):\n", " \"Plot validation loss in the pbar graph\"\n", " rec = self.learn.recorder\n", " iters = range_of(rec.losses)\n", " val_losses = [v[1] for v in rec.values]\n", " x_bounds = (0, (self.n_epoch - len(self.nb_batches)) * self.nb_batches[0] + len(rec.losses))\n", " y_bounds = (0, max((max(Tensor(rec.losses)), max(Tensor(val_losses)))))\n", " self.progress.mbar.update_graph([(iters, rec.losses), (self.nb_batches, val_losses)], x_bounds, y_bounds)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
epochtrain_lossvalid_losstime
015.1159828.12448300:00
111.6768455.93544300:00
28.2273623.91016200:00
35.2362212.50086400:00
43.2237131.59283100:00
51.9672961.01243300:00
61.2159650.64005300:00
70.7480910.40249800:00
80.4445470.26005900:00
90.2716140.17102000:00
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#slow\n", "learn = synth_learner(cbs=ShowGraphCallback())\n", "learn.fit(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## CSVLogger -" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# export\n", "class CSVLogger(Callback):\n", " run_after=Recorder\n", " \"Log the results displayed in `learn.path/fname`\"\n", " def __init__(self, fname='history.csv', append=False):\n", " self.fname,self.append = Path(fname),append\n", "\n", " def read_log(self):\n", " \"Convenience method to quickly access the log.\"\n", " return pd.read_csv(self.path/self.fname)\n", "\n", " def begin_fit(self):\n", " \"Prepare file with metric names.\"\n", " self.path.parent.mkdir(parents=True, exist_ok=True)\n", " self.file = (self.path/self.fname).open('a' if self.append else 'w')\n", " self.file.write(','.join(self.recorder.metric_names) + '\\n')\n", " self.old_logger,self.learn.logger = self.logger,self._write_line\n", "\n", " def _write_line(self, log):\n", " \"Write a line with `log` and call the old logger.\"\n", " self.file.write(','.join([str(t) for t in log]) + '\\n')\n", " self.old_logger(log)\n", "\n", " def after_fit(self):\n", " \"Close the file and clean up.\"\n", " self.file.close()\n", " self.learn.logger = self.old_logger" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The results are appened to an existing file if `append`, or they overwrite it otherwise." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
epochtrain_lossvalid_losstime
014.53560410.57363500:00
111.4056407.53513600:00
27.8654384.95257700:00
35.1549843.12102700:00
43.2724651.92519600:00
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "learn = synth_learner(cbs=CSVLogger())\n", "learn.fit(5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

CSVLogger.read_log[source]

\n", "\n", "> CSVLogger.read_log()\n", "\n", "Convenience method to quickly access the log." ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(CSVLogger.read_log)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df = learn.csv_logger.read_log()\n", "test_eq(df.columns.values, learn.recorder.metric_names)\n", "for i,v in enumerate(learn.recorder.values):\n", " test_close(df.iloc[i][:3], [i] + v)\n", "os.remove(learn.path/learn.csv_logger.fname)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

CSVLogger.begin_fit[source]

\n", "\n", "> CSVLogger.begin_fit()\n", "\n", "Prepare file with metric names." ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(CSVLogger.begin_fit)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

CSVLogger.after_fit[source]

\n", "\n", "> CSVLogger.after_fit()\n", "\n", "Close the file and clean up." ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(CSVLogger.after_fit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Export -" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Converted 00_test.ipynb.\n", "Converted 01_core.ipynb.\n", "Converted 01a_torch_core.ipynb.\n", "Converted 02_script.ipynb.\n", "Converted 03_dataloader.ipynb.\n", "Converted 04_transform.ipynb.\n", "Converted 05_data_core.ipynb.\n", "Converted 06_data_transforms.ipynb.\n", "Converted 07_vision_core.ipynb.\n", "Converted 08_pets_tutorial.ipynb.\n", "Converted 09_vision_augment.ipynb.\n", "Converted 11_layers.ipynb.\n", "Converted 11a_vision_models_xresnet.ipynb.\n", "Converted 12_optimizer.ipynb.\n", "Converted 13_learner.ipynb.\n", "Converted 14_callback_schedule.ipynb.\n", "Converted 15_callback_hook.ipynb.\n", "Converted 16_callback_progress.ipynb.\n", "Converted 17_callback_tracker.ipynb.\n", "Converted 18_callback_fp16.ipynb.\n", "Converted 19_callback_mixup.ipynb.\n", "Converted 20_metrics.ipynb.\n", "Converted 21_tutorial_imagenette.ipynb.\n", "Converted 22_vision_learner.ipynb.\n", "Converted 23_tutorial_transfer_learning.ipynb.\n", "Converted 30_text_core.ipynb.\n", "Converted 31_text_data.ipynb.\n", "Converted 32_text_models_awdlstm.ipynb.\n", "Converted 33_text_models_core.ipynb.\n", "Converted 34_callback_rnn.ipynb.\n", "Converted 35_tutorial_wikitext.ipynb.\n", "Converted 36_text_models_qrnn.ipynb.\n", "Converted 37_text_learner.ipynb.\n", "Converted 38_tutorial_ulmfit.ipynb.\n", "Converted 40_tabular_core.ipynb.\n", "Converted 41_tabular_model.ipynb.\n", "Converted 42_tabular_rapids.ipynb.\n", "Converted 50_data_block.ipynb.\n", "Converted 90_notebook_core.ipynb.\n", "Converted 91_notebook_export.ipynb.\n", "Converted 92_notebook_showdoc.ipynb.\n", "Converted 93_notebook_export2html.ipynb.\n", "Converted 94_index.ipynb.\n", "Converted 95_utils_test.ipynb.\n", "Converted 96_data_external.ipynb.\n", "Converted notebook2jekyll.ipynb.\n" ] } ], "source": [ "#hide\n", "from local.notebook.export import notebook2script\n", "notebook2script(all_fs=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 2 }